[copr] python_copr_apiv2: [python][WIP] support APIv2 - working with sub-resource (4f82cad)
by vgologuz@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : python_copr_apiv2
>---------------------------------------------------------------
commit 4f82cadbabc3e6ff76db9ac56d05005c81ed755d
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Tue Sep 22 17:24:53 2015 +0200
[python][WIP] support APIv2 - working with sub-resource
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/rest_api/schemas.py | 1 +
python/copr/client_v2/client.py | 4 +-
python/copr/client_v2/entities.py | 36 +++++-----
python/copr/client_v2/handlers.py | 72 ++++++++++++++++++--
python/copr/client_v2/resources.py | 63 ++++++++++++++----
python/copr/client_v2/schemas.py | 9 +++
python/run_tmp.py | 29 ++++++++-
7 files changed, 171 insertions(+), 43 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/rest_api/schemas.py b/frontend/coprs_frontend/coprs/rest_api/schemas.py
index 2144b54..e9db738 100644
--- a/frontend/coprs_frontend/coprs/rest_api/schemas.py
+++ b/frontend/coprs_frontend/coprs/rest_api/schemas.py
@@ -114,6 +114,7 @@ class ProjectCreateSchema(ProjectSchema):
chroots = SpaceSeparatedList(load_only=True, default=list)
+# todo: rename to ProjectChrootSchema
class CoprChrootSchema(Schema):
buildroot_pkgs = SpaceSeparatedList()
diff --git a/python/copr/client_v2/client.py b/python/copr/client_v2/client.py
index 5b680a0..7409c59 100644
--- a/python/copr/client_v2/client.py
+++ b/python/copr/client_v2/client.py
@@ -142,6 +142,8 @@ class CoprClient(UnicodeMixin):
response = self.nc.request(self.api_root)
self.root = Root.from_response(response, self.root_url)
- self.projects = ProjectHandle(self.nc, self.root.get_resource_base_url(u"projects"))
+ self.projects = ProjectHandle(self.nc, root_url=self.root_url,
+ base_url=self.root.get_resource_base_url(u"projects"),)
+
# import ipdb; ipdb.set_trace()
diff --git a/python/copr/client_v2/entities.py b/python/copr/client_v2/entities.py
index bc422e6..cac4a2d 100644
--- a/python/copr/client_v2/entities.py
+++ b/python/copr/client_v2/entities.py
@@ -2,7 +2,7 @@
from ..util import UnicodeMixin
-from .schemas import ProjectSchema, EmptySchema
+from .schemas import ProjectSchema, EmptySchema, ProjectChrootSchema
class Link(UnicodeMixin):
@@ -17,7 +17,7 @@ class Link(UnicodeMixin):
self.role, self.target_type, self.href)
@classmethod
- def parse_from_dict(cls, data_dict, map_name_to_role):
+ def from_dict(cls, data_dict, map_name_to_role):
"""
{
"self": {
@@ -57,28 +57,28 @@ class Entity(UnicodeMixin):
@classmethod
def from_dict(cls, raw_dict):
parsed = cls._schema.load(raw_dict)
- return cls(**parsed.data)
+ obj = cls()
+ for field, value in parsed.data.items():
+ setattr(obj, field, value)
+ return obj
class ProjectEntity(Entity):
- """
- :type handle: client_v2.handlers.ProjectHandle
- """
_schema = ProjectSchema(strict=True)
- def __init__(self, **kwargs):
+ def __unicode__(self):
+ return "<Project #{}: {}/{}>".format(self.id, self.owner, self.name)
+
- self.id = kwargs.get("id")
- self.name = kwargs.get("name")
- self.owner = kwargs.get("owner")
- self.description = kwargs.get("description")
- self.instructions = kwargs.get("instructions")
- self.homepage = kwargs.get("homepage")
- self.contact = kwargs.get("contact")
- self.disable_createrepo = kwargs.get("disable_creatrepo")
- self.build_enable_net = kwargs.get("build_enable_net")
- self.repos = kwargs.get("repos")
+class ProjectChrootEntity(Entity):
+
+ _schema = ProjectChrootSchema(strict=True)
def __unicode__(self):
- return "<Project #{}: {}/{}>".format(self.id, self.owner, self.name)
+ return "<Project chroot: {}, additional " \
+ "packages: {}, comps size if any: {}>".format(
+ self.name,
+ self.buildroot_pkgs,
+ self.comps_len,
+ )
diff --git a/python/copr/client_v2/handlers.py b/python/copr/client_v2/handlers.py
index f343722..6718aa7 100644
--- a/python/copr/client_v2/handlers.py
+++ b/python/copr/client_v2/handlers.py
@@ -2,22 +2,22 @@
from .common import EntityTypes
from .entities import Link
-from .resources import Project, OperationResult, ProjectsList
+from .resources import Project, OperationResult, ProjectsList, ProjectChroot, ProjectChrootList
class AbstractHandle(object):
- pass
-
-
-class ProjectHandle(AbstractHandle):
"""
- :type nc: NetClient
+ :type nc: copr.client_v2.net_client.NetClient
"""
- def __init__(self, nc, base_url):
+ def __init__(self, nc, root_url, base_url, ):
self.nc = nc
+ self.root_url = root_url
self.base_url = base_url
+
+class ProjectHandle(AbstractHandle):
+
def get_list(self, search_query=None, owner=None, name=None, limit=None, offset=None):
"""
:param search_query:
@@ -37,7 +37,7 @@ class ProjectHandle(AbstractHandle):
result = ProjectsList(
self,
response=response,
- links=Link.parse_from_dict(data_dict["_links"], {
+ links=Link.from_dict(data_dict["_links"], {
"self": EntityTypes.PROJECT,
"builds": EntityTypes.BUILD,
}),
@@ -71,6 +71,13 @@ class ProjectHandle(AbstractHandle):
options=query_params
)
+ def get_project_chroot_handle(self, project):
+ """
+ :type project: copr.client_v2.resources.Project
+ """
+ base_url = "{}{}".format(self.root_url, project.get_href_by_name("chroots"))
+ return ProjectChrootHandle(self.nc, self.root_url, base_url)
+
def update(self, project):
"""
:type project: ProjectEntity
@@ -85,3 +92,52 @@ class ProjectHandle(AbstractHandle):
url = "{}/{}".format(self.base_url, project_id)
response = self.nc.request(url, method="delete", do_auth=True)
return OperationResult(self, response)
+
+
+class ProjectChrootHandle(AbstractHandle):
+
+ def get_one(self, name):
+ """
+ :type project: copr.client_v2.resources.Project
+ :param str name: chroot name
+ """
+
+ url = "{}/{}".format(self.base_url, name)
+ response = self.nc.request(url)
+
+ return ProjectChroot.from_response(
+ handle=self,
+ response=response,
+ data_dict=response.json,
+ )
+
+ def get_list(self):
+ response = self.nc.request(self.base_url)
+ data_dict = response.json
+ return ProjectChrootList(
+ self,
+ response=response,
+ links=Link.from_dict(data_dict["_links"], {
+ "self": EntityTypes.PROJECT_CHROOT,
+ }),
+ individuals=[
+ ProjectChroot.from_response(
+ handle=self,
+ response=None,
+ data_dict=dict_part
+ )
+ for dict_part in data_dict["chroots"]
+ ]
+ )
+
+ def disable(self, name):
+ pass
+
+ def enable(self, name):
+ pass
+
+ def update(self, project_chroot):
+ """
+ :type project_chroot: copr.client_v2.entities.ProjectChrootEntity
+ """
+ pass
diff --git a/python/copr/client_v2/resources.py b/python/copr/client_v2/resources.py
index 32bc9a3..209a807 100644
--- a/python/copr/client_v2/resources.py
+++ b/python/copr/client_v2/resources.py
@@ -4,7 +4,7 @@ from collections import Iterable
from ..util import UnicodeMixin
from .common import EntityTypes
-from .entities import Link, ProjectEntity
+from .entities import Link, ProjectEntity, ProjectChrootEntity
from .schemas import EmptySchema
@@ -39,6 +39,9 @@ class IndividualResource(UnicodeMixin):
else:
raise KeyError(item)
+ def __unicode__(self):
+ return self._entity.__unicode__()
+
def get_href_by_name(self, name):
"""
:type name: str
@@ -65,7 +68,7 @@ class Root(IndividualResource):
:type root_url: unicode
"""
data_dict = response.json
- links = Link.parse_from_dict(data_dict["_links"], {
+ links = Link.from_dict(data_dict["_links"], {
"self": EntityTypes.ROOT,
"projects": EntityTypes.PROJECT,
"builds": EntityTypes.BUILD,
@@ -78,7 +81,7 @@ class Root(IndividualResource):
class Project(IndividualResource):
"""
:type entity: ProjectEntity
- :type handle: client_v2.handlers.ProjectHandle
+ :type handle: copr.client_v2.handlers.ProjectHandle
"""
def __init__(self, entity, handle, **kwargs):
super(Project, self).__init__(entity=entity, handle=handle, **kwargs)
@@ -94,16 +97,43 @@ class Project(IndividualResource):
def get_self(self):
return self._handle.get_one(self.id, **self._options)
- def __unicode__(self):
- return self._entity.__unicode__()
+ def get_project_chroot(self, name):
+ chroot_handle = self._handle.get_project_chroot_handle(self)
+ return chroot_handle.get_one(name=name)
+
+ def get_project_chroot_list(self):
+ chroot_handle = self._handle.get_project_chroot_handle(self)
+ return chroot_handle.get_list()
@classmethod
def from_response(cls, handle, response, data_dict, options=None):
- links = Link.parse_from_dict(data_dict["_links"], {
+ links = Link.from_dict(data_dict["_links"], {
"self": EntityTypes.PROJECT,
"builds": EntityTypes.BUILD,
+ "chroots": EntityTypes.PROJECT_CHROOT,
})
entity = ProjectEntity.from_dict(data_dict["project"])
+ # import ipdb; ipdb.set_trace()
+ return cls(entity=entity, handle=handle, response=response, links=links, options=options)
+
+
+class ProjectChroot(IndividualResource):
+ """
+ :type entity: copr.client_v2.entities.ProjectChrootEntity
+ :type handle: copr.client_v2.handlers.ProjectChrootHandle
+ """
+ def __init__(self, entity, handle, **kwargs):
+ super(ProjectChroot, self).__init__(entity=entity, handle=handle, **kwargs)
+ self._entity = entity
+ self._handle = handle
+
+ @classmethod
+ def from_response(cls, handle, response, data_dict, options=None):
+ links = Link.from_dict(data_dict["_links"], {
+ "self": EntityTypes.PROJECT_CHROOT,
+ "project": EntityTypes.PROJECT,
+ })
+ entity = ProjectChrootEntity.from_dict(data_dict["chroot"])
return cls(entity=entity, handle=handle, response=response, links=links, options=options)
@@ -143,15 +173,9 @@ class CollectionResource(Iterable, UnicodeMixin):
class ProjectsList(CollectionResource):
"""
- :type handle: client_v2.handlers.ProjectHandle
+ :type handle: copr.client_v2.handlers.ProjectHandle
"""
- # def __iter__(self):
- # """
- # :rtype: Iterable[ProjectEntity]
- # """
- # return super(ProjectsList, self).__iter__()
-
def __init__(self, handle, **kwargs):
super(ProjectsList, self).__init__(**kwargs)
self._handle = handle
@@ -172,3 +196,16 @@ class ProjectsList(CollectionResource):
return self._handle.get_list(self, **params)
+
+class ProjectChrootList(CollectionResource):
+ """
+ :type handle: coprclient_v2.handlers.ProjectChrootHandle
+ """
+
+ def __init__(self, handle, **kwargs):
+ super(ProjectChrootList, self).__init__(**kwargs)
+ self._handle = handle
+
+ @property
+ def chroots(self):
+ return self._individuals
diff --git a/python/copr/client_v2/schemas.py b/python/copr/client_v2/schemas.py
index 8280fe9..bf666d9 100644
--- a/python/copr/client_v2/schemas.py
+++ b/python/copr/client_v2/schemas.py
@@ -32,3 +32,12 @@ class ProjectSchema(Schema):
repos = fields.List(fields.Str())
+class ProjectChrootSchema(Schema):
+
+ name = fields.Str(load_only=True)
+ buildroot_pkgs = fields.List(fields.Str())
+
+
+ comps = fields.Str(allow_none=True)
+ comps_name = fields.Str(allow_none=True)
+ comps_len = fields.Int(load_only=True, allow_none=True)
diff --git a/python/run_tmp.py b/python/run_tmp.py
index 134e49e..2b4741c 100644
--- a/python/run_tmp.py
+++ b/python/run_tmp.py
@@ -1,5 +1,11 @@
# coding: utf-8
+from logging import basicConfig, getLogger
+
+basicConfig()
+log = getLogger(__name__)
+
+
#from copr.client_v2.client import CoprClient
# from copr.client_v2.entities import ProjectEntity
@@ -26,6 +32,8 @@ def main():
project = client.projects.get_one(2262)
#
pp(project)
+ # import ipdb; ipdb.set_trace()
+ x = 2
# res = project.update()
# print(res)
@@ -46,11 +54,26 @@ def main():
- # t1()
- t2()
+ #t1()
+ # t2()
+
+ def t4():
+ project = client.projects.get_one(3554)
+ #pp(project)
+ #print(project._response.json)
+
+ name = "fedora-21-x86_64"
+ #pc = project.get_project_chroot(name)
+ #print(pc)
+ for pc in project.get_project_chroot_list():
+ print(pc)
+ t4()
if __name__ == "__main__":
- main()
+ try:
+ main()
+ except Exception:
+ log.exception("something went wrong")
8 years, 7 months
[copr] python_copr_apiv2: [python][WIP] support APIv2 -- client architecture seems fine, going to check some corner cases, i.e.: file upload, work with embedded (d55ba16)
by vgologuz@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : python_copr_apiv2
>---------------------------------------------------------------
commit d55ba16218373c0aefd6b15307fd14224d0c686a
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Tue Sep 22 16:09:28 2015 +0200
[python][WIP] support APIv2 -- client architecture seems fine, going to check some corner cases, i.e.: file upload, work with embedded
>---------------------------------------------------------------
python/copr/__init__.py | 5 +
python/copr/client_v2/client.py | 16 +--
python/copr/client_v2/entities.py | 260 ++++--------------------------------
python/copr/client_v2/handlers.py | 87 ++++++++++++
python/copr/client_v2/resources.py | 174 ++++++++++++++++++++++++
python/copr/client_v2/schemas.py | 34 +++++
python/run_tmp.py | 63 ++++++---
7 files changed, 374 insertions(+), 265 deletions(-)
diff --git a/python/copr/__init__.py b/python/copr/__init__.py
index 4f929cd..f2c03b3 100644
--- a/python/copr/__init__.py
+++ b/python/copr/__init__.py
@@ -1 +1,6 @@
from .client import CoprClient
+
+from .client_v2.client import CoprClient as ClientV2
+
+create_client2_from_params = ClientV2.create_from_params
+create_client2_from_file_config = ClientV2.create_from_file_config
diff --git a/python/copr/client_v2/client.py b/python/copr/client_v2/client.py
index e57496b..5b680a0 100644
--- a/python/copr/client_v2/client.py
+++ b/python/copr/client_v2/client.py
@@ -17,8 +17,9 @@ import six
from six.moves import configparser
# from requests_toolbelt.multipart.encoder import MultipartEncoder
-from .entities import parse_root, ProjectHandle, RootEntity
-from client_v2.common import EntityTypes
+from .resources import Root
+from .handlers import ProjectHandle
+from .common import EntityTypes
from .net_client import NetClient
if sys.version_info < (2, 7):
@@ -32,15 +33,6 @@ from ..exceptions import CoprConfigException, CoprNoConfException, \
CoprRequestException, \
CoprUnknownResponseException
-
-
-# from .responses import ProjectHandle, \
-# CoprResponse, BuildHandle, BaseHandle, ProjectChrootHandle
-#
-# from .parsers import fabric_simple_fields_parser, ProjectListParser, \
-# CommonMsgErrorOutParser, NewBuildListParser, ProjectChrootsParser, \
-# ProjectDetailsFieldsParser
-
from ..util import UnicodeMixin
log = logging.getLogger(__name__)
@@ -149,7 +141,7 @@ class CoprClient(UnicodeMixin):
log.debug("Getting root resources")
response = self.nc.request(self.api_root)
- self.root = parse_root(response, self.root_url)
+ self.root = Root.from_response(response, self.root_url)
self.projects = ProjectHandle(self.nc, self.root.get_resource_base_url(u"projects"))
# import ipdb; ipdb.set_trace()
diff --git a/python/copr/client_v2/entities.py b/python/copr/client_v2/entities.py
index b9ef8e7..bc422e6 100644
--- a/python/copr/client_v2/entities.py
+++ b/python/copr/client_v2/entities.py
@@ -1,26 +1,21 @@
# coding: utf-8
-from collections import Iterable
-import json
+from ..util import UnicodeMixin
-from client_v2.common import EntityTypes
-from .net_client import NetClient, ResponseWrapper
+from .schemas import ProjectSchema, EmptySchema
-class Link(object):
+class Link(UnicodeMixin):
def __init__(self, role, href, target_type):
self.role = role
self.href = href
- self.target_type = target_type
+ self.target_type = target_type # not sure if we need this
def __unicode__(self):
return u"<Link: role: {}, target type: {}, href: {}".format(
self.role, self.target_type, self.href)
- def __str__(self):
- return self.__unicode__()
-
@classmethod
def parse_from_dict(cls, data_dict, map_name_to_role):
"""
@@ -49,242 +44,41 @@ class Link(object):
}
-class Individual(object):
- """
- :type handle: AbstractHandle or None
- :type response: ResponseWrapper
- :type links: (dict of (str, Link)) or None
- """
+class Entity(UnicodeMixin):
- def __init__(self, handle=None, response=None, links=None, data=None, embedded=None, options=None):
- self._handle = handle
- self._response = response
- self._links = links
- self._data = data
- self._embedded = embedded or dict()
- self._options = options or dict()
+ _schema = EmptySchema()
- def get_href_by_name(self, name):
- """
- :type name: str
- """
- return self._links[name].href
+ def to_dict(self):
+ return self._schema.dump(self)
def to_json(self):
- return "null"
-
-
-class Collection(Iterable):
- """
- :type handle: AbstractHandle or None
- :type response: ResponseWrapper
- :type links: (dict of (str, Link)) or None
- """
-
- def __init__(self, handle=None, response=None, links=None, individuals=None, options=None):
- self._handle = handle
- self._response = response
- self._links = links
- self._options = options or dict()
- self._individuals = individuals
-
- def get_href_by_name(self, name):
- """
- :type name: str
- """
- return self._links[name].href
-
- def __iter__(self):
- """
- :rtype: Iterable[Individual]
- """
- return iter(self._individuals)
-
+ return self._schema.dumps(self)
-class ProjectsList(Collection):
- """
- :type handle: ProjectHandle
- """
-
- # def __iter__(self):
- # """
- # :rtype: Iterable[ProjectEntity]
- # """
- # return super(ProjectsList, self).__iter__()
-
- def __init__(self, handle, **kwargs):
- super(ProjectsList, self).__init__(**kwargs)
- self._handle = handle
-
- @property
- def projects(self):
- return self._individuals
-
- def next_page(self):
- limit = self._options.get("limit", 100)
- offset = self._options.get("offset", 0)
-
- offset += limit
- params = {}
- params.update(self._options)
- params["limit"] = limit
- params["offset"] = offset
-
- return self._handle.get_list(self, **params)
-
-
-class OperationResult(Individual):
-
- def __init__(self, handle, response=None, data=None, new_location=None, options=None):
- super(OperationResult, self).__init__(handle, response=response, data=data, options=options)
- self.new_location = new_location
-
-
-class RootEntity(Individual):
-
- def __init__(self, response, links, root_url):
- super(RootEntity, self).__init__(response=response, links=links)
- self.root_url = root_url
-
- def get_resource_base_url(self, resource_name):
- """
- :type entity_type: client_v2.common.EntityTypes
- """
- return "{}{}".format(self.root_url, self.get_href_by_name(resource_name))
+ @classmethod
+ def from_dict(cls, raw_dict):
+ parsed = cls._schema.load(raw_dict)
+ return cls(**parsed.data)
-# TODO: replace with marshmallow
-class ProjectEntity(Individual):
+class ProjectEntity(Entity):
"""
- :type handle: ProjectHandle
+ :type handle: client_v2.handlers.ProjectHandle
"""
- def __init__(self, handle, **kwargs):
- super(ProjectEntity, self).__init__( **kwargs)
- self._handle = handle
- self.id = self._data.get("id")
- self.name = self._data.get("name")
- self.owner = self._data.get("owner")
- self.description = self._data.get("description")
- self.instructions = self._data.get("instructions")
- self.homepage = self._data.get("homepage")
- self.contact = self._data.get("contact")
- self.disable_createrepo = self._data.get("disable_creatrepo")
- self.build_enable_net = self._data.get("build_enable_net")
- self.repos = self._data.get("repos")
-
- def to_json(self):
- # todo: replace with marshmallow
- to_dump = {}
- for key in self._data.keys():
- to_dump[key] = getattr(self, key)
- return json.dumps(to_dump)
+ _schema = ProjectSchema(strict=True)
- def update(self):
- self._handle.update(self)
+ def __init__(self, **kwargs):
- def delete(self):
- self._handle.delete(self.id)
+ self.id = kwargs.get("id")
+ self.name = kwargs.get("name")
+ self.owner = kwargs.get("owner")
+ self.description = kwargs.get("description")
+ self.instructions = kwargs.get("instructions")
+ self.homepage = kwargs.get("homepage")
+ self.contact = kwargs.get("contact")
+ self.disable_createrepo = kwargs.get("disable_creatrepo")
+ self.build_enable_net = kwargs.get("build_enable_net")
+ self.repos = kwargs.get("repos")
def __unicode__(self):
return "<Project #{}: {}/{}>".format(self.id, self.owner, self.name)
-
- def __str__(self):
- return self.__unicode__()
-
-
-def parse_root(response, root_url):
- """
- :type response: ResponseWrapper
- :type root_url: unicode
- """
- data_dict = response.json
- links = Link.parse_from_dict(data_dict["_links"], {
- "self": EntityTypes.ROOT,
- "projects": EntityTypes.PROJECT,
- "builds": EntityTypes.BUILD,
- "build_tasks": EntityTypes.BUILD_TASK,
- "mock_chroots": EntityTypes.MOCK_CHROOT,
- })
- return RootEntity(response=response, links=links, root_url=root_url)
-
-
-def parse_project(handle, response, data_dict, options=None):
- links = Link.parse_from_dict(data_dict["_links"], {
- "self": EntityTypes.PROJECT,
- "builds": EntityTypes.BUILD,
- })
- return ProjectEntity(handle, response=response, links=links, data=data_dict["project"], options=options)
-
-
-class AbstractHandle(object):
- pass
-
-
-class ProjectHandle(AbstractHandle):
- """
- :type nc: NetClient
- """
- def __init__(self, nc, base_url):
-
- self.nc = nc
- self.base_url = base_url
-
- def get_list(self, search_query=None, owner=None, name=None, limit=None, offset=None):
- """
- :param search_query:
- :param owner:
- :param name:
- :param limit:
- :param offset:
- :rtype: ProjectsList
- """
- options = {}
- if limit:
- options["limit"] = limit
- # todo: add other
-
- response = self.nc.request(self.base_url, query_params=options)
- data_dict = response.json
- result = ProjectsList(
- self,
- response=response,
- links=Link.parse_from_dict(data_dict["_links"], {
- "self": EntityTypes.PROJECT,
- "builds": EntityTypes.BUILD,
- }),
- individuals=[
- parse_project(self, response, data_dict=dict_part)
- for dict_part in data_dict["projects"]
- ],
- options=None
- )
- return result
-
- def get_one(self, project_id, show_builds=False, show_chroots=False):
- """
- :type project_id: int
- """
- query_params = {
- "show_builds": show_builds,
- "show_chroots": show_chroots
- }
-
- url = "{}/{}".format(self.base_url, project_id)
- response = self.nc.request(url, query_params=query_params)
- return parse_project(self, response, data_dict=response.json, options=query_params)
-
- def update(self, project):
- """
- :type project: ProjectEntity
- """
- url = "{}/{}".format(self.base_url, project.id)
- data = project.to_json()
-
- response = self.nc.request(url, method="put", data=data, do_auth=True)
- return OperationResult(self, response)
-
- def delete(self, project_id):
- url = "{}/{}".format(self.base_url, project_id)
- response = self.nc.request(url, method="delete", do_auth=True)
- return OperationResult(self, response)
diff --git a/python/copr/client_v2/handlers.py b/python/copr/client_v2/handlers.py
new file mode 100644
index 0000000..f343722
--- /dev/null
+++ b/python/copr/client_v2/handlers.py
@@ -0,0 +1,87 @@
+# coding: utf-8
+
+from .common import EntityTypes
+from .entities import Link
+from .resources import Project, OperationResult, ProjectsList
+
+
+class AbstractHandle(object):
+ pass
+
+
+class ProjectHandle(AbstractHandle):
+ """
+ :type nc: NetClient
+ """
+ def __init__(self, nc, base_url):
+
+ self.nc = nc
+ self.base_url = base_url
+
+ def get_list(self, search_query=None, owner=None, name=None, limit=None, offset=None):
+ """
+ :param search_query:
+ :param owner:
+ :param name:
+ :param limit:
+ :param offset:
+ :rtype: ProjectsList
+ """
+ options = {}
+ if limit:
+ options["limit"] = limit
+ # todo: add other
+
+ response = self.nc.request(self.base_url, query_params=options)
+ data_dict = response.json
+ result = ProjectsList(
+ self,
+ response=response,
+ links=Link.parse_from_dict(data_dict["_links"], {
+ "self": EntityTypes.PROJECT,
+ "builds": EntityTypes.BUILD,
+ }),
+ individuals=[
+ Project.from_response(
+ handle=self,
+ response=None,
+ data_dict=dict_part,
+ )
+ for dict_part in data_dict["projects"]
+ ],
+ options=None
+ )
+ return result
+
+ def get_one(self, project_id, show_builds=False, show_chroots=False):
+ """
+ :type project_id: int
+ """
+ query_params = {
+ "show_builds": show_builds,
+ "show_chroots": show_chroots
+ }
+
+ url = "{}/{}".format(self.base_url, project_id)
+ response = self.nc.request(url, query_params=query_params)
+ return Project.from_response(
+ handle=self,
+ response=response,
+ data_dict=response.json,
+ options=query_params
+ )
+
+ def update(self, project):
+ """
+ :type project: ProjectEntity
+ """
+ url = "{}/{}".format(self.base_url, project.id)
+ data = project.to_json()
+
+ response = self.nc.request(url, method="put", data=data, do_auth=True)
+ return OperationResult(self, response)
+
+ def delete(self, project_id):
+ url = "{}/{}".format(self.base_url, project_id)
+ response = self.nc.request(url, method="delete", do_auth=True)
+ return OperationResult(self, response)
diff --git a/python/copr/client_v2/resources.py b/python/copr/client_v2/resources.py
new file mode 100644
index 0000000..32bc9a3
--- /dev/null
+++ b/python/copr/client_v2/resources.py
@@ -0,0 +1,174 @@
+# coding: utf-8
+from collections import Iterable
+
+from ..util import UnicodeMixin
+
+from .common import EntityTypes
+from .entities import Link, ProjectEntity
+from .schemas import EmptySchema
+
+
+class IndividualResource(UnicodeMixin):
+ """
+ :type handle: client_v2.handlers.AbstractHandle or None
+ :type response: ResponseWrapper or None
+ :type links: (dict of (str, Link)) or None
+ """
+ _schema = EmptySchema(strict=True)
+
+ def __init__(self, entity, handle=None, response=None, links=None, embedded=None, options=None):
+ self._entity = entity
+ self._handle = handle
+ self._response = response
+ self._links = links
+ self._embedded = embedded or dict()
+ self._options = options or dict()
+
+ def __dir__(self):
+ res = list(set(
+ dir(self.__class__) + list(self.__dict__.keys())
+ ))
+ if self._entity:
+ res.extend([x for x in dir(self._entity) if not x.startswith("_")])
+ return res
+
+ def __getattr__(self, item):
+ # import ipdb; ipdb.set_trace()
+ if hasattr(self._entity, item):
+ return getattr(self._entity, item)
+ else:
+ raise KeyError(item)
+
+ def get_href_by_name(self, name):
+ """
+ :type name: str
+ """
+ return self._links[name].href
+
+
+class Root(IndividualResource):
+
+ def __init__(self, response, links, root_url):
+ super(Root, self).__init__(entity=None, response=response, links=links)
+ self.root_url = root_url
+
+ def get_resource_base_url(self, resource_name):
+ """
+ :type entity_type: client_v2.common.EntityTypes
+ """
+ return "{}{}".format(self.root_url, self.get_href_by_name(resource_name))
+
+ @classmethod
+ def from_response(cls, response, root_url):
+ """
+ :type response: ResponseWrapper
+ :type root_url: unicode
+ """
+ data_dict = response.json
+ links = Link.parse_from_dict(data_dict["_links"], {
+ "self": EntityTypes.ROOT,
+ "projects": EntityTypes.PROJECT,
+ "builds": EntityTypes.BUILD,
+ "build_tasks": EntityTypes.BUILD_TASK,
+ "mock_chroots": EntityTypes.MOCK_CHROOT,
+ })
+ return Root(response=response, links=links, root_url=root_url)
+
+
+class Project(IndividualResource):
+ """
+ :type entity: ProjectEntity
+ :type handle: client_v2.handlers.ProjectHandle
+ """
+ def __init__(self, entity, handle, **kwargs):
+ super(Project, self).__init__(entity=entity, handle=handle, **kwargs)
+ self._entity = entity
+ self._handle = handle
+
+ def update(self):
+ return self._handle.update(self._entity)
+
+ def delete(self):
+ return self._handle.delete(self.id)
+
+ def get_self(self):
+ return self._handle.get_one(self.id, **self._options)
+
+ def __unicode__(self):
+ return self._entity.__unicode__()
+
+ @classmethod
+ def from_response(cls, handle, response, data_dict, options=None):
+ links = Link.parse_from_dict(data_dict["_links"], {
+ "self": EntityTypes.PROJECT,
+ "builds": EntityTypes.BUILD,
+ })
+ entity = ProjectEntity.from_dict(data_dict["project"])
+ return cls(entity=entity, handle=handle, response=response, links=links, options=options)
+
+
+class OperationResult(IndividualResource):
+
+ def __init__(self, handle, response=None, entity=None, new_location=None, options=None):
+ super(OperationResult, self).__init__(handle=handle, response=response, entity=entity, options=options)
+ self.new_location = new_location
+
+
+class CollectionResource(Iterable, UnicodeMixin):
+ """
+ :type handle: client_v2.handlers.AbstractHandle or None
+ :type response: ResponseWrapper
+ :type links: (dict of (str, Link)) or None
+ """
+
+ def __init__(self, handle=None, response=None, links=None, individuals=None, options=None):
+ self._handle = handle
+ self._response = response
+ self._links = links
+ self._options = options or dict()
+ self._individuals = individuals
+
+ def get_href_by_name(self, name):
+ """
+ :type name: str
+ """
+ return self._links[name].href
+
+ def __iter__(self):
+ """
+ :rtype: Iterable[IndividualResource]
+ """
+ return iter(self._individuals)
+
+
+class ProjectsList(CollectionResource):
+ """
+ :type handle: client_v2.handlers.ProjectHandle
+ """
+
+ # def __iter__(self):
+ # """
+ # :rtype: Iterable[ProjectEntity]
+ # """
+ # return super(ProjectsList, self).__iter__()
+
+ def __init__(self, handle, **kwargs):
+ super(ProjectsList, self).__init__(**kwargs)
+ self._handle = handle
+
+ @property
+ def projects(self):
+ return self._individuals
+
+ def next_page(self):
+ limit = self._options.get("limit", 100)
+ offset = self._options.get("offset", 0)
+
+ offset += limit
+ params = {}
+ params.update(self._options)
+ params["limit"] = limit
+ params["offset"] = offset
+
+ return self._handle.get_list(self, **params)
+
diff --git a/python/copr/client_v2/schemas.py b/python/copr/client_v2/schemas.py
new file mode 100644
index 0000000..8280fe9
--- /dev/null
+++ b/python/copr/client_v2/schemas.py
@@ -0,0 +1,34 @@
+# coding: utf-8
+
+from collections import Iterable
+from marshmallow import Schema, fields
+from marshmallow import Schema, fields, validates_schema, ValidationError, validate
+
+# todo: maybe define schemas for Link sets in each indvidual/collection
+# class LinkSchema(Schema):
+# href = fields.Str()
+#
+
+
+class EmptySchema(Schema):
+ pass
+
+
+class ProjectSchema(Schema):
+
+ id = fields.Int()
+ name = fields.Str()
+
+ owner = fields.Str()
+ description = fields.Str()
+ instructions = fields.Str()
+ homepage = fields.Url(allow_none=True)
+ contact = fields.Str(allow_none=True)
+
+ disable_createrepo = fields.Bool(allow_none=True)
+ build_enable_net = fields.Bool(allow_none=True)
+ last_modified = fields.DateTime(dump_only=True)
+
+ repos = fields.List(fields.Str())
+
+
diff --git a/python/run_tmp.py b/python/run_tmp.py
index 04b6771..134e49e 100644
--- a/python/run_tmp.py
+++ b/python/run_tmp.py
@@ -1,32 +1,55 @@
# coding: utf-8
-from copr.client_v2.client import CoprClient
-from copr.client_v2.entities import ProjectEntity
+#from copr.client_v2.client import CoprClient
+# from copr.client_v2.entities import ProjectEntity
+
+from copr import create_client2_from_params
def main():
copr_url = "http://copr-fe-dev.cloud.fedoraproject.org"
- client = CoprClient.create_from_params(
+ client = create_client2_from_params(
root_url=copr_url,
login="wczictdaerhmwfxolham",
token="kxcnwfmleulpnkckzspxxgwhxjolhc"
-
)
- client.post_init()
-
- project = client.projects.get_one(3260)
-
- print("Project: {} id {}, {}\n {}".format(
- project.name,
- project.id,
- project.description,
- [(x, y.href) for x, y in project._links.items()]
- ))
- # res = project._handle.update(project)
-
- plist = client.projects.get_list(limit=7)
- for p in plist:
- print(p)
- print(p.get_href_by_name("builds"))
+
+ def pp(project):
+ print("Project: {} id {}, {}\n {}\n{}".format(
+ project.name,
+ project.id,
+ project.description,
+ project.repos,
+ [(x, y.href) for x, y in project._links.items()]
+ ))
+
+ def t1():
+ project = client.projects.get_one(2262)
+ #
+ pp(project)
+
+ # res = project.update()
+ # print(res)
+ #
+ def t3(project):
+ p = project.get_self()
+ pp(p)
+
+ def t2():
+ plist = client.projects.get_list(limit=20)
+ for p in plist:
+ #print(p)
+ #print(p.get_href_by_name("builds"))
+ pp(p)
+ print("==")
+ print("==")
+ print("==")
+
+
+
+ # t1()
+ t2()
+
+
if __name__ == "__main__":
8 years, 7 months
[copr] python_copr_apiv2: [python][WIP] support APIv2 (70ff5b4)
by vgologuz@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : python_copr_apiv2
>---------------------------------------------------------------
commit 70ff5b4bcc520df1a9106dbdbf631417df67d143
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Fri Sep 18 11:57:40 2015 +0200
[python][WIP] support APIv2
>---------------------------------------------------------------
python/copr/client/__init__.py | 3 +
python/copr/client/client.py | 4 +-
python/copr/client/responses.py | 2 +-
.../src => python/copr/client_v2}/__init__.py | 0
python/copr/client_v2/client.py | 155 +++++++++++
python/copr/client_v2/common.py | 8 +
python/copr/client_v2/entities.py | 290 ++++++++++++++++++++
python/copr/client_v2/net_client.py | 128 +++++++++
python/copr/{client => }/exceptions.py | 0
python/copr/test/client_v2/test_client.py | 33 +++
python/copr/{client => }/util.py | 0
python/run_tmp.py | 33 +++
12 files changed, 653 insertions(+), 3 deletions(-)
diff --git a/python/copr/client/__init__.py b/python/copr/client/__init__.py
index 045574f..52f7c6c 100644
--- a/python/copr/client/__init__.py
+++ b/python/copr/client/__init__.py
@@ -7,3 +7,6 @@ __version__ = "0.0.1"
__description__ = "Python client for copr service."
from .client import CoprClient
+
+# for cli backward compatibility
+import copr.exceptions as exceptions
diff --git a/python/copr/client/client.py b/python/copr/client/client.py
index aa20647..40071f7 100644
--- a/python/copr/client/client.py
+++ b/python/copr/client/client.py
@@ -33,7 +33,7 @@ else:
log = logging.getLogger(__name__)
log.addHandler(NullHandler())
-from .exceptions import CoprConfigException, CoprNoConfException, \
+from ..exceptions import CoprConfigException, CoprNoConfException, \
CoprRequestException, \
CoprUnknownResponseException
@@ -44,7 +44,7 @@ from .parsers import fabric_simple_fields_parser, ProjectListParser, \
CommonMsgErrorOutParser, NewBuildListParser, ProjectChrootsParser, \
ProjectDetailsFieldsParser
-from .util import UnicodeMixin
+from ..util import UnicodeMixin
# TODO: add deco to check that login/token are provided
# and raise correct error
diff --git a/python/copr/client/responses.py b/python/copr/client/responses.py
index af46f58..23f6016 100644
--- a/python/copr/client/responses.py
+++ b/python/copr/client/responses.py
@@ -15,7 +15,7 @@ import weakref
# TODO: Add Response for collections?
# TODO: what about copr_project-> chroot_list, build_list, repos_list
#
-from .util import UnicodeMixin
+from ..util import UnicodeMixin
class CoprResponse(UnicodeMixin):
diff --git a/builder_image/src/__init__.py b/python/copr/client_v2/__init__.py
similarity index 100%
copy from builder_image/src/__init__.py
copy to python/copr/client_v2/__init__.py
diff --git a/python/copr/client_v2/client.py b/python/copr/client_v2/client.py
new file mode 100644
index 0000000..e57496b
--- /dev/null
+++ b/python/copr/client_v2/client.py
@@ -0,0 +1,155 @@
+# coding: utf-8
+
+from __future__ import print_function
+from __future__ import unicode_literals
+from __future__ import division
+from __future__ import absolute_import
+
+import json
+import sys
+import os
+import logging
+from marshmallow import pprint
+
+import requests
+import six
+
+from six.moves import configparser
+# from requests_toolbelt.multipart.encoder import MultipartEncoder
+
+from .entities import parse_root, ProjectHandle, RootEntity
+from client_v2.common import EntityTypes
+from .net_client import NetClient
+
+if sys.version_info < (2, 7):
+ class NullHandler(logging.Handler):
+ def emit(self, record):
+ pass
+else:
+ from logging import NullHandler
+
+from ..exceptions import CoprConfigException, CoprNoConfException, \
+ CoprRequestException, \
+ CoprUnknownResponseException
+
+
+
+# from .responses import ProjectHandle, \
+# CoprResponse, BuildHandle, BaseHandle, ProjectChrootHandle
+#
+# from .parsers import fabric_simple_fields_parser, ProjectListParser, \
+# CommonMsgErrorOutParser, NewBuildListParser, ProjectChrootsParser, \
+# ProjectDetailsFieldsParser
+
+from ..util import UnicodeMixin
+
+log = logging.getLogger(__name__)
+log.addHandler(NullHandler())
+
+
+class CoprClient(UnicodeMixin):
+ """ Main interface to the copr service
+
+ :param NetClient net_client: wrapper for http requests
+ :param unicode root_url: used as copr projects root
+ :param bool no_config: helper flag to indicate that no config was provided
+
+ Could be created:
+ - using static method :py:meth:`CoprClient.create_from_file_config`
+ - using static method :py:meth:`CoprClient.create_from_params`
+
+ If you create Client directly call post_init() method after the creation.
+ """
+
+ def __init__(self, net_client, root_url=None, no_config=False):
+ """
+
+ """
+ self.nc = net_client
+ self.root_url = root_url or u"http://copr.fedoraproject.org"
+
+ self.no_config = no_config
+
+ self._main_resources = None
+
+ self.root = None
+ """:type : RootEntity or None """
+
+ self.projects = None
+ """:type : ProjectHandle or None """
+
+ def __unicode__(self):
+ return (
+ u"<Copr client. api root url: {}, config provided: {}, net client: {}>"
+ .format(self.root_url, not self.no_config, self.nc)
+ )
+
+ @property
+ def api_root(self):
+ """
+ Url to API endpoint
+ """
+ return "{0}/api_2".format(self.root_url)
+
+ @classmethod
+ def create_from_params(cls, root_url=None, login=None, token=None):
+ nc = NetClient(login, token)
+ client = cls(nc, root_url, no_config=True)
+ client.post_init()
+ return client
+
+
+ @classmethod
+ def create_from_file_config(cls, filepath=None, ignore_error=False):
+ """
+ Creates Copr client using the information from the config file.
+
+ :param filepath: specifies config location,
+ default: "~/.config/copr"
+ :type filepath: `str`
+ :param bool ignore_error: When true and config is missing,
+ creates default Client without credentionals
+
+ :rtype: :py:class:`~.client.CoprClient`
+ """
+ raw_config = configparser.ConfigParser()
+ if not filepath:
+ filepath = os.path.join(os.path.expanduser("~"), ".config", "copr")
+ config = {}
+ if not raw_config.read(filepath):
+ log.warning(
+ "No configuration file '~/.config/copr' found. "
+ "See man copr-cli for more information")
+ config["no_config"] = True
+ if not ignore_error:
+ raise CoprNoConfException()
+ else:
+ client = cls.create_from_params()
+ else:
+ try:
+ for field in ["login", "token", "copr_url"]:
+ if six.PY3:
+ config[field] = raw_config["copr-cli"].get(field, None)
+ else:
+ config[field] = raw_config.get("copr-cli", field, None)
+ nc = NetClient(config["login"], config["token"])
+ client = cls(nc, root_url=config["copr_url"], )
+
+ except configparser.Error as err:
+ if not ignore_error:
+ raise CoprConfigException(
+ "Bad configuration file: {0}".format(err))
+ else:
+ client = cls.create_from_params()
+
+ client.post_init()
+ return client
+
+ def post_init(self):
+ log.debug("Getting root resources")
+ response = self.nc.request(self.api_root)
+
+ self.root = parse_root(response, self.root_url)
+ self.projects = ProjectHandle(self.nc, self.root.get_resource_base_url(u"projects"))
+
+ # import ipdb; ipdb.set_trace()
diff --git a/python/copr/client_v2/common.py b/python/copr/client_v2/common.py
new file mode 100644
index 0000000..4e96bf5
--- /dev/null
+++ b/python/copr/client_v2/common.py
@@ -0,0 +1,8 @@
+# coding: utf-8
+class EntityTypes(object):
+ ROOT = "root"
+ PROJECT = "project"
+ PROJECT_CHROOT = "project_chroot"
+ BUILD = "build"
+ BUILD_TASK = "build_task"
+ MOCK_CHROOT = "mock_chroot"
diff --git a/python/copr/client_v2/entities.py b/python/copr/client_v2/entities.py
new file mode 100644
index 0000000..b9ef8e7
--- /dev/null
+++ b/python/copr/client_v2/entities.py
@@ -0,0 +1,290 @@
+# coding: utf-8
+
+from collections import Iterable
+import json
+
+from client_v2.common import EntityTypes
+from .net_client import NetClient, ResponseWrapper
+
+
+class Link(object):
+
+ def __init__(self, role, href, target_type):
+ self.role = role
+ self.href = href
+ self.target_type = target_type
+
+ def __unicode__(self):
+ return u"<Link: role: {}, target type: {}, href: {}".format(
+ self.role, self.target_type, self.href)
+
+ def __str__(self):
+ return self.__unicode__()
+
+ @classmethod
+ def parse_from_dict(cls, data_dict, map_name_to_role):
+ """
+ {
+ "self": {
+ "href": "/api_2/projects/2482?show_builds=True&show_chroots=True"
+ },
+ "chroots": {
+ "href": "/api_2/projects/2482/chroots"
+ },
+ "builds": {
+ "href": "/api_2/builds?project_id=2482"
+ }
+ }
+
+ return
+ {
+ <entity type> -> Link()
+ }
+ """
+ return {
+ role_name:
+ cls(role_name, definition["href"], map_name_to_role[role_name])
+ for role_name, definition in data_dict.items()
+ if role_name in map_name_to_role
+ }
+
+
+class Individual(object):
+ """
+ :type handle: AbstractHandle or None
+ :type response: ResponseWrapper
+ :type links: (dict of (str, Link)) or None
+ """
+
+ def __init__(self, handle=None, response=None, links=None, data=None, embedded=None, options=None):
+ self._handle = handle
+ self._response = response
+ self._links = links
+ self._data = data
+ self._embedded = embedded or dict()
+ self._options = options or dict()
+
+ def get_href_by_name(self, name):
+ """
+ :type name: str
+ """
+ return self._links[name].href
+
+ def to_json(self):
+ return "null"
+
+
+class Collection(Iterable):
+ """
+ :type handle: AbstractHandle or None
+ :type response: ResponseWrapper
+ :type links: (dict of (str, Link)) or None
+ """
+
+ def __init__(self, handle=None, response=None, links=None, individuals=None, options=None):
+ self._handle = handle
+ self._response = response
+ self._links = links
+ self._options = options or dict()
+ self._individuals = individuals
+
+ def get_href_by_name(self, name):
+ """
+ :type name: str
+ """
+ return self._links[name].href
+
+ def __iter__(self):
+ """
+ :rtype: Iterable[Individual]
+ """
+ return iter(self._individuals)
+
+
+class ProjectsList(Collection):
+ """
+ :type handle: ProjectHandle
+ """
+
+ # def __iter__(self):
+ # """
+ # :rtype: Iterable[ProjectEntity]
+ # """
+ # return super(ProjectsList, self).__iter__()
+
+ def __init__(self, handle, **kwargs):
+ super(ProjectsList, self).__init__(**kwargs)
+ self._handle = handle
+
+ @property
+ def projects(self):
+ return self._individuals
+
+ def next_page(self):
+ limit = self._options.get("limit", 100)
+ offset = self._options.get("offset", 0)
+
+ offset += limit
+ params = {}
+ params.update(self._options)
+ params["limit"] = limit
+ params["offset"] = offset
+
+ return self._handle.get_list(self, **params)
+
+
+class OperationResult(Individual):
+
+ def __init__(self, handle, response=None, data=None, new_location=None, options=None):
+ super(OperationResult, self).__init__(handle, response=response, data=data, options=options)
+ self.new_location = new_location
+
+
+class RootEntity(Individual):
+
+ def __init__(self, response, links, root_url):
+ super(RootEntity, self).__init__(response=response, links=links)
+ self.root_url = root_url
+
+ def get_resource_base_url(self, resource_name):
+ """
+ :type entity_type: client_v2.common.EntityTypes
+ """
+ return "{}{}".format(self.root_url, self.get_href_by_name(resource_name))
+
+
+# TODO: replace with marshmallow
+class ProjectEntity(Individual):
+ """
+ :type handle: ProjectHandle
+ """
+ def __init__(self, handle, **kwargs):
+ super(ProjectEntity, self).__init__( **kwargs)
+ self._handle = handle
+
+ self.id = self._data.get("id")
+ self.name = self._data.get("name")
+ self.owner = self._data.get("owner")
+ self.description = self._data.get("description")
+ self.instructions = self._data.get("instructions")
+ self.homepage = self._data.get("homepage")
+ self.contact = self._data.get("contact")
+ self.disable_createrepo = self._data.get("disable_creatrepo")
+ self.build_enable_net = self._data.get("build_enable_net")
+ self.repos = self._data.get("repos")
+
+ def to_json(self):
+ # todo: replace with marshmallow
+ to_dump = {}
+ for key in self._data.keys():
+ to_dump[key] = getattr(self, key)
+ return json.dumps(to_dump)
+
+ def update(self):
+ self._handle.update(self)
+
+ def delete(self):
+ self._handle.delete(self.id)
+
+ def __unicode__(self):
+ return "<Project #{}: {}/{}>".format(self.id, self.owner, self.name)
+
+ def __str__(self):
+ return self.__unicode__()
+
+
+def parse_root(response, root_url):
+ """
+ :type response: ResponseWrapper
+ :type root_url: unicode
+ """
+ data_dict = response.json
+ links = Link.parse_from_dict(data_dict["_links"], {
+ "self": EntityTypes.ROOT,
+ "projects": EntityTypes.PROJECT,
+ "builds": EntityTypes.BUILD,
+ "build_tasks": EntityTypes.BUILD_TASK,
+ "mock_chroots": EntityTypes.MOCK_CHROOT,
+ })
+ return RootEntity(response=response, links=links, root_url=root_url)
+
+
+def parse_project(handle, response, data_dict, options=None):
+ links = Link.parse_from_dict(data_dict["_links"], {
+ "self": EntityTypes.PROJECT,
+ "builds": EntityTypes.BUILD,
+ })
+ return ProjectEntity(handle, response=response, links=links, data=data_dict["project"], options=options)
+
+
+class AbstractHandle(object):
+ pass
+
+
+class ProjectHandle(AbstractHandle):
+ """
+ :type nc: NetClient
+ """
+ def __init__(self, nc, base_url):
+
+ self.nc = nc
+ self.base_url = base_url
+
+ def get_list(self, search_query=None, owner=None, name=None, limit=None, offset=None):
+ """
+ :param search_query:
+ :param owner:
+ :param name:
+ :param limit:
+ :param offset:
+ :rtype: ProjectsList
+ """
+ options = {}
+ if limit:
+ options["limit"] = limit
+ # todo: add other
+
+ response = self.nc.request(self.base_url, query_params=options)
+ data_dict = response.json
+ result = ProjectsList(
+ self,
+ response=response,
+ links=Link.parse_from_dict(data_dict["_links"], {
+ "self": EntityTypes.PROJECT,
+ "builds": EntityTypes.BUILD,
+ }),
+ individuals=[
+ parse_project(self, response, data_dict=dict_part)
+ for dict_part in data_dict["projects"]
+ ],
+ options=None
+ )
+ return result
+
+ def get_one(self, project_id, show_builds=False, show_chroots=False):
+ """
+ :type project_id: int
+ """
+ query_params = {
+ "show_builds": show_builds,
+ "show_chroots": show_chroots
+ }
+
+ url = "{}/{}".format(self.base_url, project_id)
+ response = self.nc.request(url, query_params=query_params)
+ return parse_project(self, response, data_dict=response.json, options=query_params)
+
+ def update(self, project):
+ """
+ :type project: ProjectEntity
+ """
+ url = "{}/{}".format(self.base_url, project.id)
+ data = project.to_json()
+
+ response = self.nc.request(url, method="put", data=data, do_auth=True)
+ return OperationResult(self, response)
+
+ def delete(self, project_id):
+ url = "{}/{}".format(self.base_url, project_id)
+ response = self.nc.request(url, method="delete", do_auth=True)
+ return OperationResult(self, response)
diff --git a/python/copr/client_v2/net_client.py b/python/copr/client_v2/net_client.py
new file mode 100644
index 0000000..26aeccd
--- /dev/null
+++ b/python/copr/client_v2/net_client.py
@@ -0,0 +1,128 @@
+# coding: utf-8
+import json
+
+from logging import getLogger
+
+from requests import request, ConnectionError
+
+
+log = getLogger(__name__)
+
+
+class RequestError(Exception):
+ def __init__(self, msg, url, request_kwargs, response=None):
+ self.msg = msg
+ self.url = url
+ self.request_kwargs = request_kwargs
+ self.response = response
+
+ @property
+ def response_json(self):
+ if self.response is None:
+ raise ValueError("No response")
+ try:
+ result = json.loads(self.response.text)
+ except (ValueError, AttributeError):
+ raise ValueError("Malformed response, couldn't get json content")
+
+ return result
+
+ def __unicode__(self):
+ res = "Error occurred while accessing {}: {}\n".format(
+ self.url, self.msg)
+ if self.response is not None:
+ res += "code {}: {}\n".format(self.response.status_code, self.response_json["message"])
+ return res
+
+ def __str__(self):
+ return self.__unicode__()
+
+
+class NetworkError(RequestError):
+ def __init__(self, url, request_kwargs, requests_error):
+ self.requests_error = requests_error
+ super(NetworkError, self).__init__(
+ u"Connection error", url, request_kwargs, response=None)
+
+ def __unicode__(self):
+ res = super(NetworkError, self).__unicode__()
+ res += u"Original error: {}\n".format(self.requests_error)
+
+
+class AuthError(RequestError):
+ def __init__(self, url, request_kwargs, response):
+ super(AuthError, self).__init__("Authorization failed",
+ url, request_kwargs, response)
+
+
+class ResponseWrapper(object):
+
+ def __init__(self, response):
+ """
+ :raises ValueError: when fails to deserialize json content
+ """
+ self.response = response
+ if response.status_code != 204:
+ self.json = response.json()
+ else:
+ self.json = None
+
+ @property
+ def status_code(self):
+ return self.response.status_code
+
+ @property
+ def headers(self):
+ return self.response.headers
+
+
+class NetClient(object):
+ """
+ Abstraction around python-requests
+
+ :param unicode login: login for BasicAuth
+ :param unicode password: password for BasicAuth
+ """
+
+ def __init__(self, login=None, password=None):
+ self.login = login
+ self.token = password
+
+ def request(self, url, method=None, query_params=None, data=None, do_auth=False):
+
+ if method is None:
+ method = "get"
+ elif method not in ["get", "post", "delete", "put"]:
+ raise Exception("Method {0} not allowed".format(method))
+
+ kwargs = {}
+ if do_auth:
+ if self.login is None or self.token is None:
+ raise RequestError("Credentionals for BasicAuth not set, request aborted",
+ url, kwargs)
+ kwargs["auth"] = (self.login, self.token)
+ if query_params:
+ kwargs["params"] = query_params
+ if data:
+ kwargs["data"] = data
+
+ try:
+ response = request(
+ method=method.upper(),
+ url=url,
+ **kwargs
+ )
+ log.debug("raw response: {0}".format(response.text))
+ except ConnectionError as e:
+ raise NetworkError(url, kwargs, e)
+
+ if response.status_code == 403:
+ raise AuthError(url, kwargs, response)
+
+ if response.status_code > 399:
+ raise RequestError("", url, kwargs, response)
+
+ try:
+ return ResponseWrapper(response)
+ except ValueError:
+ raise RequestError("Failed to parse server response", url, kwargs, response)
diff --git a/python/copr/client/exceptions.py b/python/copr/exceptions.py
similarity index 100%
rename from python/copr/client/exceptions.py
rename to python/copr/exceptions.py
diff --git a/python/copr/test/client_v2/test_client.py b/python/copr/test/client_v2/test_client.py
new file mode 100644
index 0000000..34bc49e
--- /dev/null
+++ b/python/copr/test/client_v2/test_client.py
@@ -0,0 +1,33 @@
+# coding: utf-8
+import os
+import copy
+import tarfile
+import tempfile
+import shutil
+import time
+
+import six
+
+
+if six.PY3:
+ from unittest import mock
+ from unittest.mock import MagicMock
+else:
+ import mock
+ from mock import MagicMock
+
+import pytest
+
+(a)pytest.yield_fixture
+def mc_requests():
+ with mock.patch('backend.createrepo.Popen') as handle:
+ yield handle
+
+
+
+class TestClient(object):
+
+ def setup_method(self, method):
+
+ self.requests_patcher = mock.patch('')
+
diff --git a/python/copr/client/util.py b/python/copr/util.py
similarity index 100%
rename from python/copr/client/util.py
rename to python/copr/util.py
diff --git a/python/run_tmp.py b/python/run_tmp.py
new file mode 100644
index 0000000..04b6771
--- /dev/null
+++ b/python/run_tmp.py
@@ -0,0 +1,33 @@
+# coding: utf-8
+
+from copr.client_v2.client import CoprClient
+from copr.client_v2.entities import ProjectEntity
+
+def main():
+ copr_url = "http://copr-fe-dev.cloud.fedoraproject.org"
+ client = CoprClient.create_from_params(
+ root_url=copr_url,
+ login="wczictdaerhmwfxolham",
+ token="kxcnwfmleulpnkckzspxxgwhxjolhc"
+
+ )
+ client.post_init()
+
+ project = client.projects.get_one(3260)
+
+ print("Project: {} id {}, {}\n {}".format(
+ project.name,
+ project.id,
+ project.description,
+ [(x, y.href) for x, y in project._links.items()]
+ ))
+ # res = project._handle.update(project)
+
+ plist = client.projects.get_list(limit=7)
+ for p in plist:
+ print(p)
+ print(p.get_href_by_name("builds"))
+
+
+if __name__ == "__main__":
+ main()
8 years, 7 months
[copr] master: add group model (086d232)
by Miroslav Suchý
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : master
>---------------------------------------------------------------
commit 086d232426800ac326083b601be05e057ec6b00a
Author: Miroslav Suchý <msuchy(a)redhat.com>
Date: Mon Sep 21 14:41:12 2015 +0200
add group model
>---------------------------------------------------------------
.../versions/3f4966a9cc0_add_group_table.py | 30 ++++++++++++++++++++
frontend/coprs_frontend/coprs/models.py | 15 ++++++++++
2 files changed, 45 insertions(+), 0 deletions(-)
diff --git a/frontend/coprs_frontend/alembic/versions/3f4966a9cc0_add_group_table.py b/frontend/coprs_frontend/alembic/versions/3f4966a9cc0_add_group_table.py
new file mode 100644
index 0000000..c468158
--- /dev/null
+++ b/frontend/coprs_frontend/alembic/versions/3f4966a9cc0_add_group_table.py
@@ -0,0 +1,30 @@
+"""Add group table
+
+Revision ID: 3f4966a9cc0
+Revises: 3b4cfc666d14
+Create Date: 2015-09-24 10:14:06.291886
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '3f4966a9cc0'
+down_revision = '3b4cfc666d14'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ ### commands auto generated by Alembic ###
+ op.create_table('group',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('name', sa.String(length=127), nullable=True),
+ sa.Column('fas_name', sa.String(length=127), nullable=True),
+ sa.PrimaryKeyConstraint('id')
+ ### end Alembic commands ###
+
+
+def downgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('group')
+ ### end Alembic commands ###
diff --git a/frontend/coprs_frontend/coprs/models.py b/frontend/coprs_frontend/coprs/models.py
index 63ef2bd..3f8e834 100644
--- a/frontend/coprs_frontend/coprs/models.py
+++ b/frontend/coprs_frontend/coprs/models.py
@@ -160,6 +160,8 @@ class Copr(db.Model, helpers.Serializer):
# relations
owner_id = db.Column(db.Integer, db.ForeignKey("user.id"))
owner = db.relationship("User", backref=db.backref("coprs"))
+ group_id = db.Column(db.Integer, db.ForeignKey("group.id"))
+ group = db.relationship("Group", backref=db.backref("groups"))
mock_chroots = association_proxy("copr_chroots", "mock_chroot")
# enable networking for the builds by default
@@ -853,3 +855,16 @@ class CounterStat(db.Model, helpers.Serializer):
counter = db.Column(db.Integer, default=0, server_default="0")
+class Group(db.Model, helpers.Serializer):
+ """
+ Represents FAS groups and their aliases in Copr
+ """
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(127))
+ fas_name = db.Column(db.String(127))
+
+ def __str__(self):
+ return self.__unicode__()
+
+ def __unicode__(self):
+ return "{0} ({1})".format(self.name, self.fas_name)
8 years, 7 months
[copr] frontend-python3: [frontend] do the str/bytes/unicode py2/3 compatibility magic (1fc1771)
by frostyx@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : frontend-python3
>---------------------------------------------------------------
commit 1fc1771bdcefd7926d379c7637e468a1400afe12
Author: Jakub Kadlčík <jkadlcik(a)redhat.com>
Date: Tue Sep 22 17:35:03 2015 +0200
[frontend] do the str/bytes/unicode py2/3 compatibility magic
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/rest_api/common.py | 2 +-
.../coprs/views/api_ns/api_general.py | 2 +-
frontend/coprs_frontend/coprs/views/misc.py | 7 +-
frontend/coprs_frontend/tests/coprs_test_case.py | 10 ++--
.../coprs_frontend/tests/test_api/test_build_r.py | 40 +++++-----
.../tests/test_api/test_build_task_r.py | 12 ++--
.../tests/test_api/test_mock_chroot_r.py | 6 +-
.../tests/test_api/test_project_chroot_r.py | 26 ++++----
.../tests/test_api/test_project_r.py | 20 +++---
.../test_views/test_admin/test_admin_general.py | 6 +-
.../test_views/test_api_ns/test_api_general.py | 8 +-
.../test_backend_ns/test_backend_general.py | 26 ++++----
.../test_views/test_coprs_ns/test_coprs_builds.py | 20 +++---
.../test_views/test_coprs_ns/test_coprs_general.py | 74 ++++++++++----------
14 files changed, 130 insertions(+), 129 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/rest_api/common.py b/frontend/coprs_frontend/coprs/rest_api/common.py
index 061627d..6be90b2 100644
--- a/frontend/coprs_frontend/coprs/rest_api/common.py
+++ b/frontend/coprs_frontend/coprs/rest_api/common.py
@@ -91,7 +91,7 @@ def rest_api_auth_required(f):
base64string = flask.request.headers["Authorization"]
base64string = base64string.split()[1].strip()
userstring = base64.b64decode(base64string)
- (api_login, token) = userstring.split(":")
+ (api_login, token) = userstring.split(b":")
except Exception:
log.exception("Failed to get auth token from headers")
api_login = token = None
diff --git a/frontend/coprs_frontend/coprs/views/api_ns/api_general.py b/frontend/coprs_frontend/coprs/views/api_ns/api_general.py
index ee6e493..743b36e 100644
--- a/frontend/coprs_frontend/coprs/views/api_ns/api_general.py
+++ b/frontend/coprs_frontend/coprs/views/api_ns/api_general.py
@@ -47,7 +47,7 @@ def api_new_token():
"""
user = flask.g.user
- copr64 = base64.b64encode("copr") + "##"
+ copr64 = base64.b64encode(b"copr") + b"##"
api_login = helpers.generate_api_token(
flask.current_app.config["API_TOKEN_LENGTH"] - len(copr64))
user.api_login = api_login
diff --git a/frontend/coprs_frontend/coprs/views/misc.py b/frontend/coprs_frontend/coprs/views/misc.py
index b0a98d7..37ce246 100644
--- a/frontend/coprs_frontend/coprs/views/misc.py
+++ b/frontend/coprs_frontend/coprs/views/misc.py
@@ -30,7 +30,7 @@ def create_user_wrapper(username, email, timezone=None):
datetime.timedelta(
days=flask.current_app.config["API_TOKEN_EXPIRATION"])
- copr64 = base64.b64encode("copr") + "##"
+ copr64 = base64.b64encode(b"copr") + b"##"
user = models.User(username=username, mail=email,
timezone=timezone,
api_login=copr64 + helpers.generate_api_token(
@@ -42,7 +42,8 @@ def create_user_wrapper(username, email, timezone=None):
def fed_raw_name(oidname):
- return oidname.replace(".id.fedoraproject.org/", "") \
+ return oidname.decode("utf-8") \
+ .replace(".id.fedoraproject.org/", "") \
.replace("http://", "")
@@ -204,7 +205,7 @@ def api_login_required(f):
base64string = flask.request.headers["Authorization"]
base64string = base64string.split()[1].strip()
userstring = base64.b64decode(base64string)
- (apt_login, token) = userstring.split(":")
+ (apt_login, token) = userstring.split(b":")
token_auth = False
if token and apt_login:
user = UsersLogic.get_by_api_login(apt_login).first()
diff --git a/frontend/coprs_frontend/tests/coprs_test_case.py b/frontend/coprs_frontend/tests/coprs_test_case.py
index 35a670d..49848b5 100644
--- a/frontend/coprs_frontend/tests/coprs_test_case.py
+++ b/frontend/coprs_frontend/tests/coprs_test_case.py
@@ -80,8 +80,8 @@ class CoprsTestCase(object):
@property
def auth_header(self):
- return {"Authorization": "Basic " +
- base64.b64encode("doesntmatter:{0}".format(self.backend_passwd))}
+ return {"Authorization": b"Basic " +
+ base64.b64encode("doesntmatter:{0}".format(self.backend_passwd).encode("utf-8"))}
@pytest.fixture
def f_db(self):
@@ -411,9 +411,9 @@ class CoprsTestCase(object):
return self.tc.open(url, **kwargs)
def _get_auth_string(self, login, token):
- userstring = "{}:{}".format(login, token)
+ userstring = "{}:{}".format(login, token).encode("utf-8")
base64string_user = base64.b64encode(userstring)
- base64string = "Basic " + base64string_user
+ base64string = b"Basic " + base64string_user
return base64string
@@ -453,6 +453,6 @@ class TransactionDecorator(object):
def wrapper(fn, fn_self, *args):
with fn_self.tc as fn_self.test_client:
with fn_self.test_client.session_transaction() as session:
- session["openid"] = getattr(fn_self, self.user).username
+ session["openid"] = getattr(fn_self, self.user).username.encode("utf-8")
return fn(fn_self, *args)
return decorator.decorator(wrapper, fn)
diff --git a/frontend/coprs_frontend/tests/test_api/test_build_r.py b/frontend/coprs_frontend/tests/test_api/test_build_r.py
index ae5bee3..00b600c 100644
--- a/frontend/coprs_frontend/tests/test_api/test_build_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_build_r.py
@@ -1,5 +1,5 @@
# coding: utf-8
-from cStringIO import StringIO
+from io import BytesIO
import json
import math
import random
@@ -27,7 +27,7 @@ class TestBuildResource(CoprsTestCase):
self.db.session.commit()
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
# not a pure test, but we test API here
builds = BuildsLogic.get_multiple().all()
@@ -48,8 +48,8 @@ class TestBuildResource(CoprsTestCase):
assert r_a.status_code == 200
assert r_b.status_code == 200
- obj_a = json.loads(r_a.data)
- obj_b = json.loads(r_b.data)
+ obj_a = json.loads(r_a.data.decode("utf-8"))
+ obj_b = json.loads(r_b.data.decode("utf-8"))
builds = BuildsLogic.get_multiple().all()
expected_ids_a = set([b.id for b in builds if b.ended_on is not None])
@@ -67,7 +67,7 @@ class TestBuildResource(CoprsTestCase):
self.db.session.commit()
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
# not a pure test, but we test API here
builds = [
@@ -86,7 +86,7 @@ class TestBuildResource(CoprsTestCase):
self.db.session.commit()
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
# not a pure test, but we test API here
builds = [
@@ -108,7 +108,7 @@ class TestBuildResource(CoprsTestCase):
href = "/api_2/builds?limit={}".format(lim)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
builds = obj["builds"]
assert len(builds) == lim
@@ -123,8 +123,8 @@ class TestBuildResource(CoprsTestCase):
assert r1.status_code == 200
assert r2.status_code == 200
- obj1 = json.loads(r1.data)
- obj2 = json.loads(r2.data)
+ obj1 = json.loads(r1.data.decode("utf-8"))
+ obj2 = json.loads(r2.data.decode("utf-8"))
assert builds[:delta] == obj1["builds"]
assert builds[delta:2 * delta] == obj2["builds"]
@@ -191,7 +191,7 @@ class TestBuildResource(CoprsTestCase):
assert r0.status_code == 201
r1 = self.tc.get(r0.headers["Location"])
assert r1.status_code == 200
- build_obj = json.loads(r1.data)
+ build_obj = json.loads(r1.data.decode("utf-8"))
build_dict = build_obj["build"]
assert build_dict["source_metadata"]["url"] == \
metadata["srpm_url"]
@@ -252,7 +252,7 @@ class TestBuildResource(CoprsTestCase):
}
data = {
"metadata": json.dumps(metadata),
- "srpm": (StringIO(u'my file contents'), 'hello world.src.rpm')
+ "srpm": (BytesIO(b'my file contents'), 'hello world.src.rpm')
}
login = self.u2.api_login
token = self.u2.api_token
@@ -280,7 +280,7 @@ class TestBuildResource(CoprsTestCase):
}
data = {
"metadata": json.dumps(metadata),
- "srpm": (StringIO(u'my file contents'), 'hello world.src.rpm')
+ "srpm": (BytesIO(b'my file contents'), 'hello world.src.rpm')
}
self.db.session.commit()
r0 = self.request_rest_api_with_auth(
@@ -303,7 +303,7 @@ class TestBuildResource(CoprsTestCase):
}
data = {
"metadata": json.dumps(metadata),
- "srpm": (StringIO(u'my file contents'), 'hello world.src.rpm')
+ "srpm": (BytesIO(b'my file contents'), 'hello world.src.rpm')
}
self.db.session.commit()
r0 = self.request_rest_api_with_auth(
@@ -315,13 +315,13 @@ class TestBuildResource(CoprsTestCase):
assert r0.status_code == 201
r1 = self.tc.get(r0.headers["Location"])
assert r1.status_code == 200
- build_obj = json.loads(r1.data)
+ build_obj = json.loads(r1.data.decode("utf-8"))
assert build_obj["build"]["source_type"] == "srpm_upload"
tasks_href = build_obj["_links"]["build_tasks"]["href"]
r2 = self.tc.get(tasks_href)
- build_chroots_obj = json.loads(r2.data)
+ build_chroots_obj = json.loads(r2.data.decode("utf-8"))
build_chroots_names = set([bc["build_task"]["chroot_name"] for bc in
build_chroots_obj["build_tasks"]])
assert set(chroot_name_list) == build_chroots_names
@@ -350,7 +350,7 @@ class TestBuildResource(CoprsTestCase):
self,f_users, f_coprs, f_builds, f_db,
f_users_api, f_mock_chroots):
data = {
- "srpm": (StringIO(u'my file contents'), 'hello world.src.rpm')
+ "srpm": (BytesIO(b'my file contents'), 'hello world.src.rpm')
}
self.db.session.commit()
r0 = self.request_rest_api_with_auth(
@@ -371,7 +371,7 @@ class TestBuildResource(CoprsTestCase):
href = "/api_2/builds/{}".format(b_id)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["build"]["id"] == b_id
assert obj["_links"]["self"]["href"] == href
@@ -385,7 +385,7 @@ class TestBuildResource(CoprsTestCase):
href = "/api_2/builds/{}?show_build_tasks=True".format(b_id)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["build"]["id"] == b_id
assert obj["_links"]["self"]["href"] == href
assert "build_tasks" in obj
@@ -520,7 +520,7 @@ class TestBuildResource(CoprsTestCase):
r2 = self.tc.get(r.headers["Location"])
assert r2.status_code == 200
- obj = json.loads(r2.data)
+ obj = json.loads(r2.data.decode("utf-8"))
assert obj["build"]["state"] == "canceled"
def test_build_put_cancel_wrong_state(
@@ -547,6 +547,6 @@ class TestBuildResource(CoprsTestCase):
r2 = self.tc.get(href)
assert r2.status_code == 200
- obj = json.loads(r2.data)
+ obj = json.loads(r2.data.decode("utf-8"))
assert obj["build"]["state"] == old_state
diff --git a/frontend/coprs_frontend/tests/test_api/test_build_task_r.py b/frontend/coprs_frontend/tests/test_api/test_build_task_r.py
index 1ffc37e..31b1034 100644
--- a/frontend/coprs_frontend/tests/test_api/test_build_task_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_build_task_r.py
@@ -26,7 +26,7 @@ class TestBuildTaskResource(CoprsTestCase):
r0 = self.tc.get(href)
assert r0.status_code == 200
- obj = json.loads(r0.data)
+ obj = json.loads(r0.data.decode("utf-8"))
assert len(obj["build_tasks"]) == len(bc_list)
def test_collection_ok_default_limit(self, f_users, f_coprs, f_mock_chroots, f_builds, f_db,
@@ -41,7 +41,7 @@ class TestBuildTaskResource(CoprsTestCase):
r0 = self.tc.get(href)
assert r0.status_code == 200
- obj = json.loads(r0.data)
+ obj = json.loads(r0.data.decode("utf-8"))
assert obj["_links"]["self"]["href"] == expected
def test_collection_ok_by_state(
@@ -64,7 +64,7 @@ class TestBuildTaskResource(CoprsTestCase):
r0 = self.tc.get(href)
assert r0.status_code == 200
- obj = json.loads(r0.data)
+ obj = json.loads(r0.data.decode("utf-8"))
assert len(obj["build_tasks"]) == len(expected_chroots)
assert set(bt["build_task"]["chroot_name"]
for bt in obj["build_tasks"]) == expected_chroots
@@ -82,7 +82,7 @@ class TestBuildTaskResource(CoprsTestCase):
r0 = self.tc.get(href)
assert r0.status_code == 200
- obj = json.loads(r0.data)
+ obj = json.loads(r0.data.decode("utf-8"))
assert len(obj["build_tasks"]) == len(bc_list)
assert obj["_links"]["self"]["href"] == href
@@ -101,7 +101,7 @@ class TestBuildTaskResource(CoprsTestCase):
r0 = self.tc.get(href)
assert r0.status_code == 200
- obj = json.loads(r0.data)
+ obj = json.loads(r0.data.decode("utf-8"))
assert len(obj["build_tasks"]) == bc_list_len
assert obj["_links"]["self"]["href"] == href
@@ -134,7 +134,7 @@ class TestBuildTaskResource(CoprsTestCase):
r0 = self.tc.get(href)
assert r0.status_code == 200
- obj = json.loads(r0.data)
+ obj = json.loads(r0.data.decode("utf-8"))
for k, res_k in expected_fields:
if res_k is None:
res_k = k
diff --git a/frontend/coprs_frontend/tests/test_api/test_mock_chroot_r.py b/frontend/coprs_frontend/tests/test_api/test_mock_chroot_r.py
index beb3568..16d6c3f 100644
--- a/frontend/coprs_frontend/tests/test_api/test_mock_chroot_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_mock_chroot_r.py
@@ -18,7 +18,7 @@ class TestMockChrootResource(CoprsTestCase):
href = "/api_2/mock_chroots"
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["_links"]["self"]["href"] == href
assert len(obj["chroots"]) == len(self.mc_basic_list)
@@ -31,7 +31,7 @@ class TestMockChrootResource(CoprsTestCase):
href = "/api_2/mock_chroots?active_only=True"
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["_links"]["self"]["href"] == href
assert len(obj["chroots"]) == expected_len
@@ -48,6 +48,6 @@ class TestMockChrootResource(CoprsTestCase):
href = "/api_2/mock_chroots/{}".format(chroot_name)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["_links"]["self"]["href"] == href
assert obj["chroot"]["name"] == chroot_name
diff --git a/frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py b/frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py
index fdb20fa..054b37f 100644
--- a/frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_project_chroot_r.py
@@ -23,7 +23,7 @@ class TestProjectChrootResource(CoprsTestCase):
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == init_len
+ assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == init_len
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots/{}".format(chroot_name),
@@ -33,7 +33,7 @@ class TestProjectChrootResource(CoprsTestCase):
r2 = self.tc.get("/api_2/projects/1/chroots")
assert r2.status_code == 200
- assert len(json.loads(r2.data)["chroots"]) == init_len - 1
+ assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == init_len - 1
# test idempotency
r3 = self.request_rest_api_with_auth(
@@ -43,7 +43,7 @@ class TestProjectChrootResource(CoprsTestCase):
assert r3.status_code == 204
r4 = self.tc.get("/api_2/projects/1/chroots")
assert r4.status_code == 200
- assert len(json.loads(r4.data)["chroots"]) == init_len - 1
+ assert len(json.loads(r4.data.decode("utf-8"))["chroots"]) == init_len - 1
def test_remove_chroot_other_user(
self, f_users, f_coprs,f_db, f_users_api,
@@ -57,7 +57,7 @@ class TestProjectChrootResource(CoprsTestCase):
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == init_len
+ assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == init_len
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots/{}".format(chroot_name),
@@ -68,7 +68,7 @@ class TestProjectChrootResource(CoprsTestCase):
r2 = self.tc.get("/api_2/projects/1/chroots")
assert r2.status_code == 200
- assert len(json.loads(r2.data)["chroots"]) == init_len
+ assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == init_len
def test_put_correct(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):
chroot_name = self.mc1.name
@@ -170,7 +170,7 @@ class TestProjectChrootResource(CoprsTestCase):
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == 1
+ assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots/{}".format(chroot_name),
@@ -196,7 +196,7 @@ class TestProjectChrootResource(CoprsTestCase):
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == 1
+ assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots",
@@ -215,7 +215,7 @@ class TestProjectChrootResource(CoprsTestCase):
for k, v in base_data.items():
assert response_data.get(k) == v
- assert_content(json.loads(r3.data)["chroot"])
+ assert_content(json.loads(r3.data.decode("utf-8"))["chroot"])
def test_create_chroot_build_no_buildchroot(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):
new_chroot_name = self.mc2.name
@@ -232,7 +232,7 @@ class TestProjectChrootResource(CoprsTestCase):
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == 1
+ assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1
def test_create_chroot_missing_name(self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):
base_data = {
@@ -243,7 +243,7 @@ class TestProjectChrootResource(CoprsTestCase):
self.db.session.commit()
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == 1
+ assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots",
@@ -254,7 +254,7 @@ class TestProjectChrootResource(CoprsTestCase):
r2 = self.tc.get("/api_2/projects/1/chroots")
assert r2.status_code == 200
- assert len(json.loads(r2.data)["chroots"]) == 1
+ assert len(json.loads(r2.data.decode("utf-8"))["chroots"]) == 1
def test_create_chroot_non_existing_name(
self, f_users, f_coprs, f_db, f_users_api, f_mock_chroots):
@@ -272,7 +272,7 @@ class TestProjectChrootResource(CoprsTestCase):
self.db.session.commit()
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == 1
+ assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots",
@@ -301,7 +301,7 @@ class TestProjectChrootResource(CoprsTestCase):
self.db.session.commit()
r0 = self.tc.get("/api_2/projects/1/chroots")
assert r0.status_code == 200
- assert len(json.loads(r0.data)["chroots"]) == 1
+ assert len(json.loads(r0.data.decode("utf-8"))["chroots"]) == 1
r1 = self.request_rest_api_with_auth(
"/api_2/projects/1/chroots",
diff --git a/frontend/coprs_frontend/tests/test_api/test_project_r.py b/frontend/coprs_frontend/tests/test_api/test_project_r.py
index dff95fe..34c4617 100644
--- a/frontend/coprs_frontend/tests/test_api/test_project_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_project_r.py
@@ -35,7 +35,7 @@ class TestProjectResource(CoprsTestCase):
href = "/api_2/projects"
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["_links"]["self"]["href"] == href
def test_project_list_all(self, f_users, f_mock_chroots, f_coprs, f_db):
@@ -43,7 +43,7 @@ class TestProjectResource(CoprsTestCase):
href = "/api_2/projects"
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert set(p["project"]["id"] for p in obj["projects"]) == \
expected_id_set
@@ -55,7 +55,7 @@ class TestProjectResource(CoprsTestCase):
href = "/api_2/projects?owner={}".format(self.u1.username)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert set(p["project"]["id"] for p in obj["projects"]) == \
expected_id_set
@@ -67,7 +67,7 @@ class TestProjectResource(CoprsTestCase):
href = "/api_2/projects?name={}".format(self.c1.name)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert set(p["project"]["id"] for p in obj["projects"]) == \
expected_id_set
@@ -85,7 +85,7 @@ class TestProjectResource(CoprsTestCase):
for href, expected in zip(href_list, [s_1, s_2, s_3]):
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert set(p["project"]["id"] for p in obj["projects"]) == \
expected
@@ -107,7 +107,7 @@ class TestProjectResource(CoprsTestCase):
r0 = self.tc.get(u"/api_2/projects?search_query={}".format(self.prefix))
assert r0.status_code == 200
- obj = json.loads(r0.data)
+ obj = json.loads(r0.data.decode("utf-8"))
assert len(obj["projects"]) == k1 + k2
for p in obj["projects"]:
assert self.prefix in p["project"]["name"]
@@ -115,7 +115,7 @@ class TestProjectResource(CoprsTestCase):
r1 = self.tc.get(u"/api_2/projects?search_query={}&owner={}"
.format(self.prefix, c1_username))
assert r1.status_code == 200
- obj = json.loads(r1.data)
+ obj = json.loads(r1.data.decode("utf-8"))
assert len(obj["projects"]) == k1
for p in obj["projects"]:
assert self.prefix in p["project"]["name"]
@@ -254,7 +254,7 @@ class TestProjectResource(CoprsTestCase):
href = "/api_2/projects/{}".format(p_id)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["project"]["id"] == p_id
assert obj["_links"]["self"]["href"] == href
@@ -266,7 +266,7 @@ class TestProjectResource(CoprsTestCase):
href = "/api_2/projects/{}?show_chroots=True".format(p_id)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["project"]["id"] == p_id
assert obj["_links"]["self"]["href"] == href
@@ -282,7 +282,7 @@ class TestProjectResource(CoprsTestCase):
href = "/api_2/projects/{}?show_builds=True".format(p_id)
r = self.tc.get(href)
assert r.status_code == 200
- obj = json.loads(r.data)
+ obj = json.loads(r.data.decode("utf-8"))
assert obj["project"]["id"] == p_id
assert obj["_links"]["self"]["href"] == href
diff --git a/frontend/coprs_frontend/tests/test_views/test_admin/test_admin_general.py b/frontend/coprs_frontend/tests/test_views/test_admin/test_admin_general.py
index 7489374..3e68ae0 100644
--- a/frontend/coprs_frontend/tests/test_views/test_admin/test_admin_general.py
+++ b/frontend/coprs_frontend/tests/test_views/test_admin/test_admin_general.py
@@ -9,10 +9,10 @@ class TestAdminLogin(CoprsTestCase):
def test_nonadmin_cant_login(self, f_users, f_db):
with self.tc as c:
with c.session_transaction() as s:
- s["openid"] = self.u2.username
+ s["openid"] = self.u2.username.encode("utf-8")
r = c.get("/admin/", follow_redirects=True)
- assert self.text_to_check not in r.data
+ assert self.text_to_check not in r.data.decode("utf-8")
def test_admin_can_login(self, f_users, f_db):
with self.tc as c:
@@ -20,4 +20,4 @@ class TestAdminLogin(CoprsTestCase):
s["openid"] = self.u1.username
r = c.get("/admin/", follow_redirects=True)
- assert self.text_to_check in r.data
+ assert self.text_to_check in r.data.decode("utf-8")
diff --git a/frontend/coprs_frontend/tests/test_views/test_api_ns/test_api_general.py b/frontend/coprs_frontend/tests/test_views/test_api_ns/test_api_general.py
index 833d9af..e71e4c0 100644
--- a/frontend/coprs_frontend/tests/test_views/test_api_ns/test_api_general.py
+++ b/frontend/coprs_frontend/tests/test_views/test_api_ns/test_api_general.py
@@ -18,8 +18,8 @@ class TestCreateCopr(CoprsTestCase):
def post_api_with_auth(self, url, content):
userstring = "{}:{}".format(self.u1.api_login, self.u1.api_token)
- base64string_user = base64.b64encode(userstring)
- base64string = "Basic " + base64string_user
+ base64string_user = base64.b64encode(userstring.encode("utf-8"))
+ base64string = b"Basic " + base64string_user
return self.tc.post(
url,
@@ -52,7 +52,7 @@ class TestCreateCopr(CoprsTestCase):
"/api/coprs/{}/new/".format(self.u1.name),
content_encoded
)
- response = json.loads(r.data)
+ response = json.loads(r.data.decode("utf-8"))
assert "New project was successfully created" in response["message"]
copr = self.models.Copr.query.filter(self.models.Copr.name == self.copr_name).one()
@@ -84,7 +84,7 @@ class TestCreateCopr(CoprsTestCase):
"/api/coprs/{}/new/".format(self.u1.name),
content_encoded
)
- response = json.loads(r.data)
+ response = json.loads(r.data.decode("utf-8"))
assert "New project was successfully created" in response["message"]
copr = self.models.Copr.query.filter(self.models.Copr.name == self.copr_name).one()
diff --git a/frontend/coprs_frontend/tests/test_views/test_backend_ns/test_backend_general.py b/frontend/coprs_frontend/tests/test_views/test_backend_ns/test_backend_general.py
index 3b8a748..bc4cddd 100644
--- a/frontend/coprs_frontend/tests/test_views/test_backend_ns/test_backend_general.py
+++ b/frontend/coprs_frontend/tests/test_views/test_backend_ns/test_backend_general.py
@@ -6,7 +6,7 @@ from tests.coprs_test_case import CoprsTestCase
class TestWaitingBuilds(CoprsTestCase):
def test_no_waiting_builds(self):
- assert '"builds": []' in self.tc.get(
+ assert b'"builds": []' in self.tc.get(
"/backend/waiting/", headers=self.auth_header).data
def test_waiting_build_only_lists_not_started_or_ended(
@@ -18,7 +18,7 @@ class TestWaitingBuilds(CoprsTestCase):
self.db.session.commit()
r = self.tc.get("/backend/waiting/", headers=self.auth_header)
- assert len(json.loads(r.data)["builds"]) == 5
+ assert len(json.loads(r.data.decode("utf-8"))["builds"]) == 5
# status = 0 # failure
@@ -88,7 +88,7 @@ class TestUpdateBuilds(CoprsTestCase):
r = self.tc.post("/backend/update/",
content_type="application/json",
data="")
- assert "You have to provide the correct password" in r.data
+ assert b"You have to provide the correct password" in r.data
# todo: add test for `backend/starting_build/`
@@ -99,8 +99,8 @@ class TestUpdateBuilds(CoprsTestCase):
content_type="application/json",
headers=self.auth_header,
data=self.data2)
- assert json.loads(r.data)["updated_builds_ids"] == [1]
- assert json.loads(r.data)["non_existing_builds_ids"] == []
+ assert json.loads(r.data.decode("utf-8"))["updated_builds_ids"] == [1]
+ assert json.loads(r.data.decode("utf-8"))["non_existing_builds_ids"] == []
updated = self.models.Build.query.filter(
self.models.Build.id == 1).one()
@@ -120,8 +120,8 @@ class TestUpdateBuilds(CoprsTestCase):
headers=self.auth_header,
data=self.data3)
- assert sorted(json.loads(r.data)["updated_builds_ids"]) == [1, 2]
- assert sorted(json.loads(r.data)["non_existing_builds_ids"]) == [
+ assert sorted(json.loads(r.data.decode("utf-8"))["updated_builds_ids"]) == [1, 2]
+ assert sorted(json.loads(r.data.decode("utf-8"))["non_existing_builds_ids"]) == [
123321, 1234321]
started = self.models.Build.query.filter(
@@ -138,14 +138,14 @@ class TestUpdateBuilds(CoprsTestCase):
class TestWaitingActions(CoprsTestCase):
def test_no_waiting_actions(self):
- assert '"actions": []' in self.tc.get(
+ assert b'"actions": []' in self.tc.get(
"/backend/waiting/", headers=self.auth_header).data
def test_waiting_actions_only_lists_not_started_or_ended(
self, f_users, f_coprs, f_actions, f_db):
r = self.tc.get("/backend/waiting/", headers=self.auth_header)
- assert len(json.loads(r.data)["actions"]) == 2
+ assert len(json.loads(r.data.decode("utf-8"))["actions"]) == 2
class TestUpdateActions(CoprsTestCase):
@@ -189,8 +189,8 @@ class TestUpdateActions(CoprsTestCase):
content_type="application/json",
headers=self.auth_header,
data=self.data1)
- assert json.loads(r.data)["updated_actions_ids"] == [1]
- assert json.loads(r.data)["non_existing_actions_ids"] == []
+ assert json.loads(r.data.decode("utf-8"))["updated_actions_ids"] == [1]
+ assert json.loads(r.data.decode("utf-8"))["non_existing_actions_ids"] == []
updated = self.models.Action.query.filter(
self.models.Action.id == 1).first()
@@ -205,8 +205,8 @@ class TestUpdateActions(CoprsTestCase):
content_type="application/json",
headers=self.auth_header,
data=self.data2)
- assert sorted(json.loads(r.data)["updated_actions_ids"]) == [1, 2]
- assert json.loads(r.data)["non_existing_actions_ids"] == [100]
+ assert sorted(json.loads(r.data.decode("utf-8"))["updated_actions_ids"]) == [1, 2]
+ assert json.loads(r.data.decode("utf-8"))["non_existing_actions_ids"] == [100]
updated = self.models.Action.query.filter(
self.models.Action.id == 1).first()
diff --git a/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_builds.py b/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_builds.py
index 42ee9af..2fa1290 100644
--- a/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_builds.py
+++ b/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_builds.py
@@ -11,7 +11,7 @@ class TestCoprShowBuilds(CoprsTestCase):
r = self.tc.get(
"/coprs/{0}/{1}/builds/".format(self.u2.name, self.c2.name))
- assert r.data.count('<tr class="build-') == 2
+ assert r.data.count(b'<tr class="build-') == 2
class TestCoprAddBuild(CoprsTestCase):
@@ -80,7 +80,7 @@ class TestCoprAddBuild(CoprsTestCase):
r = self.tc.get(
"/coprs/{0}/{1}/add_build/".format(self.u1.name, "foo"))
- assert '<input checked id="enable_net" name="enable_net"' not in r.data
+ assert b'<input checked id="enable_net" name="enable_net"' not in r.data
class TestCoprCancelBuild(CoprsTestCase):
@@ -140,7 +140,7 @@ class TestCoprDeleteBuild(CoprsTestCase):
.format(self.u1.name, self.c1.name, self.b1.id),
data={},
follow_redirects=True)
- assert "Build has been deleted" in r.data
+ assert b"Build has been deleted" in r.data
b = (self.models.Build.query.filter(
self.models.Build.id == self.b1.id)
.first())
@@ -187,7 +187,7 @@ class TestCoprDeleteBuild(CoprsTestCase):
.format(self.u1.name, self.c1.name, self.b1.id),
data={},
follow_redirects=True)
- assert "not allowed to delete build" in r.data
+ assert b"not allowed to delete build" in r.data
b = (self.models.Build.query.filter(
self.models.Build.id == self.b1.id)
.first())
@@ -236,7 +236,7 @@ class TestCoprRepeatBuild(CoprsTestCase):
assert r.status_code == 200
# no longer using URL
#assert self.b_few_chroots.pkgs in r.data
- assert "Resubmit build {}".format(self.b_few_chroots.id) in r.data
+ assert b"Resubmit build {}".format(self.b_few_chroots.id) in r.data
# TODO: maybe test, that only failed chroots are selected
@@ -264,7 +264,7 @@ class TestCoprRepeatBuild(CoprsTestCase):
data={},
follow_redirects=True)
- assert "Resubmit build {}".format(self.b1.id) in r.data
+ assert b"Resubmit build {}".format(self.b1.id) in r.data
assert r.status_code == 200
# assert "Build was resubmitted" in r.data
# assert len(self.models.Build.query
@@ -285,7 +285,7 @@ class TestCoprRepeatBuild(CoprsTestCase):
follow_redirects=True)
# import ipdb; ipdb.set_trace()
- assert "You are not allowed to repeat this build." in r.data
+ assert b"You are not allowed to repeat this build." in r.data
@TransactionDecorator("u1")
def test_copr_build_package_urls(self, f_users,
@@ -305,6 +305,6 @@ class TestCoprRepeatBuild(CoprsTestCase):
for i, url in enumerate(urls):
r.insert(i, self.test_client.post(route, data={"pkgs": url}, follow_redirects=True))
- assert "New build has been created" in r[0].data
- assert "doesn't seem to be a valid URL" in r[1].data
- assert "doesn't seem to be a valid SRPM URL" in r[2].data
+ assert b"New build has been created" in r[0].data
+ assert b"doesn't seem to be a valid URL" in r[1].data
+ assert b"doesn't seem to be a valid SRPM URL" in r[2].data
diff --git a/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py b/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
index 91a3afc..ca0ab85 100644
--- a/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
+++ b/frontend/coprs_frontend/tests/test_views/test_coprs_ns/test_coprs_general.py
@@ -49,11 +49,11 @@ class TestMonitor(CoprsTestCase):
class TestCoprsShow(CoprsTestCase):
def test_show_no_entries(self):
- assert "No projects..." in self.tc.get("/").data
+ assert b"No projects..." in self.tc.get("/").data
def test_show_more_entries(self, f_users, f_coprs, f_db):
r = self.tc.get("/")
- assert r.data.count('<!--copr-project-->') == 3
+ assert r.data.count(b'<!--copr-project-->') == 3
class TestCoprsOwned(CoprsTestCase):
@@ -62,13 +62,13 @@ class TestCoprsOwned(CoprsTestCase):
def test_owned_none(self, f_users, f_coprs, f_db):
self.db.session.add(self.u3)
r = self.test_client.get("/coprs/{0}/".format(self.u3.name))
- assert "No projects..." in r.data
+ assert b"No projects..." in r.data
@TransactionDecorator("u1")
def test_owned_one(self, f_users, f_coprs, f_db):
self.db.session.add(self.u1)
r = self.test_client.get("/coprs/{0}/".format(self.u1.name))
- assert r.data.count('<!--copr-project-->') == 1
+ assert r.data.count(b'<!--copr-project-->') == 1
class TestCoprsAllowed(CoprsTestCase):
@@ -77,13 +77,13 @@ class TestCoprsAllowed(CoprsTestCase):
def test_allowed_none(self, f_users, f_coprs, f_copr_permissions, f_db):
self.db.session.add(self.u3)
r = self.test_client.get("/coprs/{0}/allowed/".format(self.u3.name))
- assert "No projects..." in r.data
+ assert b"No projects..." in r.data
@TransactionDecorator("u1")
def test_allowed_one(self, f_users, f_coprs, f_copr_permissions, f_db):
self.db.session.add(self.u1)
r = self.test_client.get("/coprs/{0}/allowed/".format(self.u1.name))
- assert r.data.count('<!--copr-project-->') == 1
+ assert r.data.count(b'<!--copr-project-->') == 1
class TestCoprNew(CoprsTestCase):
@@ -143,7 +143,7 @@ class TestCoprNew(CoprsTestCase):
self.db.session.add(self.c1)
assert len(self.models.Copr.query.filter(
self.models.Copr.name == self.c1.name).all()) == foocoprs
- assert "You already have project named" in r.data
+ assert b"You already have project named" in r.data
@TransactionDecorator("u1")
def test_copr_new_with_initial_pkgs(self, f_users, f_mock_chroots, f_db):
@@ -164,7 +164,7 @@ class TestCoprNew(CoprsTestCase):
assert self.models.Build.query.first().copr == copr
assert self.models.Build.query.first().enable_net is True
assert copr.build_count == 1
- assert "Initial packages were successfully submitted" in r.data
+ assert b"Initial packages were successfully submitted" in r.data
@TransactionDecorator("u1")
@@ -186,7 +186,7 @@ class TestCoprNew(CoprsTestCase):
assert self.models.Build.query.first().copr == copr
assert self.models.Build.query.first().enable_net is False
assert copr.build_count == 1
- assert "Initial packages were successfully submitted" in r.data
+ assert b"Initial packages were successfully submitted" in r.data
@TransactionDecorator("u1")
def test_copr_new_is_allowed_even_if_deleted_has_same_name(
@@ -222,29 +222,29 @@ class TestCoprDetail(CoprsTestCase):
def test_copr_detail_normal(self, f_users, f_coprs, f_db):
r = self.tc.get("/coprs/{0}/{1}/".format(self.u1.name, self.c1.name))
assert r.status_code == 200
- assert self.c1.name in r.data
+ assert self.c1.name.encode("utf-8") in r.data
def test_copr_detail_contains_builds(self, f_users, f_coprs,
f_mock_chroots, f_builds, f_db):
r = self.tc.get(
"/coprs/{0}/{1}/builds/".format(self.u1.name, self.c1.name))
- assert r.data.count('<tr class="build') == 2
+ assert r.data.count(b'<tr class="build') == 2
def test_copr_detail_anonymous_doesnt_contain_permissions_table_when_no_permissions(
self, f_users, f_coprs, f_copr_permissions, f_db):
r = self.tc.get(
"/coprs/{0}/{1}/permissions/".format(self.u1.name, self.c1.name))
- assert '<!--permissions-table-->' not in r.data
+ assert b'<!--permissions-table-->' not in r.data
def test_copr_detail_contains_permissions_table(self, f_users, f_coprs,
f_copr_permissions, f_db):
r = self.tc.get(
"/coprs/{0}/{1}/permissions/".format(self.u2.name, self.c3.name))
- assert '<!--permissions-table-->' in r.data
- assert '<td>{0}'.format(self.u3.name) in r.data
- assert '<td>{0}'.format(self.u1.name) in r.data
+ assert b'<!--permissions-table-->' in r.data
+ assert '<td>{0}'.format(self.u3.name).encode("utf-8") in r.data
+ assert '<td>{0}'.format(self.u1.name).encode("utf-8") in r.data
@TransactionDecorator("u2")
def test_detail_has_correct_permissions_form(self, f_users, f_coprs,
@@ -254,14 +254,14 @@ class TestCoprDetail(CoprsTestCase):
r = self.test_client.get(
"/coprs/{0}/{1}/permissions/".format(self.u2.name, self.c3.name))
- assert r.data.count("nothing") == 2
- assert '<select id="copr_builder_1" name="copr_builder_1">' in r.data
- assert '<select id="copr_admin_1" name="copr_admin_1">' in r.data
+ assert r.data.count(b"nothing") == 2
+ assert b'<select id="copr_builder_1" name="copr_builder_1">' in r.data
+ assert b'<select id="copr_admin_1" name="copr_admin_1">' in r.data
def test_copr_detail_doesnt_show_cancel_build_for_anonymous(self, f_users, f_coprs, f_builds, f_db):
r = self.tc.get(
"/coprs/{0}/{1}/build/{2}/".format(self.u2.name, self.c2.name, self.c2.builds[0].id))
- assert "/cancel_build/" not in r.data
+ assert b"/cancel_build/" not in r.data
@TransactionDecorator("u1")
def test_copr_detail_doesnt_allow_non_submitter_to_cancel_build(
@@ -270,7 +270,7 @@ class TestCoprDetail(CoprsTestCase):
self.db.session.add_all([self.u2, self.c2])
r = self.test_client.get(
"/coprs/{0}/{1}/build/{2}/".format(self.u2.name, self.c2.name, self.c2.builds[0].id))
- assert "/cancel_build/" not in r.data
+ assert b"/cancel_build/" not in r.data
@TransactionDecorator("u2")
def test_copr_detail_allows_submitter_to_cancel_build(
@@ -279,7 +279,7 @@ class TestCoprDetail(CoprsTestCase):
self.db.session.add_all([self.u2, self.c2])
r = self.test_client.get(
"/coprs/{0}/{1}/build/{2}/".format(self.u2.name, self.c2.name, self.c2.builds[0].id))
- assert "/cancel_build/" in r.data
+ assert b"/cancel_build/" in r.data
class TestCoprEdit(CoprsTestCase):
@@ -292,7 +292,7 @@ class TestCoprEdit(CoprsTestCase):
# TODO: use some kind of html parsing library to look
# for the hidden input, this ties us
# to the precise format of the tag
- assert ('<input hidden id="id" name="id" type="hidden" value="{0}">'
+ assert (b'<input hidden id="id" name="id" type="hidden" value="{0}">'
.format(self.c1.id) in r.data)
@@ -308,7 +308,7 @@ class TestCoprUpdate(CoprsTestCase):
"id": self.c1.id},
follow_redirects=True)
- assert "Project has been updated successfully" in r.data
+ assert b"Project has been updated successfully" in r.data
@TransactionDecorator("u1")
def test_copr_admin_can_update(self, f_users, f_coprs,
@@ -322,7 +322,7 @@ class TestCoprUpdate(CoprsTestCase):
"id": self.c3.id},
follow_redirects=True)
- assert "Project has been updated successfully" in r.data
+ assert b"Project has been updated successfully" in r.data
@TransactionDecorator("u1")
def test_update_multiple_chroots(self, f_users, f_coprs,
@@ -338,7 +338,7 @@ class TestCoprUpdate(CoprsTestCase):
"id": self.c1.id},
follow_redirects=True)
- assert "Project has been updated successfully" in r.data
+ assert b"Project has been updated successfully" in r.data
self.c1 = self.db.session.merge(self.c1)
self.mc1 = self.db.session.merge(self.mc1)
self.mc2 = self.db.session.merge(self.mc2)
@@ -372,7 +372,7 @@ class TestCoprUpdate(CoprsTestCase):
"id": self.c2.id},
follow_redirects=True)
- assert "Project has been updated successfully" in r.data
+ assert b"Project has been updated successfully" in r.data
self.c2 = self.db.session.merge(self.c2)
self.mc1 = self.db.session.merge(self.mc1)
mock_chroots = (self.models.MockChroot.query
@@ -447,7 +447,7 @@ class TestCoprApplyForPermissions(CoprsTestCase):
data={"copr_builder": "1"},
follow_redirects=True)
- assert "Successfuly updated" in r.data
+ assert b"Successfuly updated" in r.data
self.u1 = self.db.session.merge(self.u1)
self.u2 = self.db.session.merge(self.u2)
@@ -469,7 +469,7 @@ class TestCoprApplyForPermissions(CoprsTestCase):
.format(self.u2.name, self.c2.name),
data={"copr_builder": 1, "copr_admin": "1"},
follow_redirects=True)
- assert "Successfuly updated" in r.data
+ assert b"Successfuly updated" in r.data
self.u1 = self.db.session.merge(self.u1)
self.c2 = self.db.session.merge(self.c2)
@@ -553,7 +553,7 @@ class TestCoprUpdatePermissions(CoprsTestCase):
"copr_admin_3": "2"},
follow_redirects=True)
- assert "Project permissions were updated" in r.data
+ assert b"Project permissions were updated" in r.data
@TransactionDecorator("u1")
def test_copr_admin_can_give_up_his_permissions(self, f_users, f_coprs,
@@ -580,7 +580,7 @@ class TestCoprUpdatePermissions(CoprsTestCase):
.first())
assert perm.copr_admin == 1
- assert "Project permissions were updated" in r.data
+ assert b"Project permissions were updated" in r.data
class TestCoprDelete(CoprsTestCase):
@@ -593,7 +593,7 @@ class TestCoprDelete(CoprsTestCase):
data={"verify": "yes"},
follow_redirects=True)
- assert "Project has been deleted successfully" in r.data
+ assert b"Project has been deleted successfully" in r.data
self.db.session.add(self.c1)
assert self.models.Action.query.first().id == self.c1.id
assert self.models.Copr.query.filter(
@@ -609,7 +609,7 @@ class TestCoprDelete(CoprsTestCase):
data={"verify": "no"},
follow_redirects=True)
- assert "Project has been deleted successfully" not in r.data
+ assert b"Project has been deleted successfully" not in r.data
assert not self.models.Action.query.first()
assert self.models.Copr.query.filter(
self.models.Copr.id == self.c1.id).first()
@@ -622,7 +622,7 @@ class TestCoprDelete(CoprsTestCase):
data={"verify": "yes"},
follow_redirects=True)
self.c1 = self.db.session.merge(self.c1)
- assert "Project has been deleted successfully" not in r.data
+ assert b"Project has been deleted successfully" not in r.data
assert not self.models.Action.query.first()
assert self.models.Copr.query.filter(
self.models.Copr.id == self.c1.id).first()
@@ -679,13 +679,13 @@ class TestCoprRepoGeneration(CoprsTestCase):
def test_fail_on_missing_dash(self):
r = self.tc.get("/coprs/reponamewithoutdash/repo/")
assert r.status_code == 404
- assert "Project repo does not exist" in r.data
+ assert b"Project repo does not exist" in r.data
def test_fail_on_nonexistent_copr(self):
r = self.tc.get(
"/coprs/bogus-user/bogus-nonexistent-repo/repo/fedora-18-x86_64/")
assert r.status_code == 404
- assert "does not exist" in r.data
+ assert b"does not exist" in r.data
def test_fail_on_no_finished_builds(self, f_users, f_coprs, f_mock_chroots,
f_not_finished_builds, f_db):
@@ -695,7 +695,7 @@ class TestCoprRepoGeneration(CoprsTestCase):
.format(self.u1.name, self.c1.name))
assert r.status_code == 404
- assert "Repository not initialized" in r.data
+ assert b"Repository not initialized" in r.data
def test_works_on_older_builds(self, f_users, f_coprs, f_mock_chroots,
f_custom_builds, f_db):
@@ -707,7 +707,7 @@ class TestCoprRepoGeneration(CoprsTestCase):
.format(self.u1.name, self.c1.name))
assert r.status_code == 200
- assert "baseurl=https://bar.baz" in r.data
+ assert b"baseurl=https://bar.baz" in r.data
app.config["ENFORCE_PROTOCOL_FOR_BACKEND_URL"] = orig
8 years, 7 months
[copr] frontend-python3: [frontend] use dict items() instead of iteritems() (5eb81eb)
by frostyx@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : frontend-python3
>---------------------------------------------------------------
commit 5eb81ebf3d2ce747dec5f017d718fd7df38d0ccd
Author: Jakub Kadlčík <jkadlcik(a)redhat.com>
Date: Tue Sep 22 17:03:29 2015 +0200
[frontend] use dict items() instead of iteritems()
>---------------------------------------------------------------
.../coprs_frontend/coprs/templates/layout.html | 2 +-
frontend/coprs_frontend/tests/test_helpers.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/templates/layout.html b/frontend/coprs_frontend/coprs/templates/layout.html
index b21eb9a..b2827bd 100644
--- a/frontend/coprs_frontend/coprs/templates/layout.html
+++ b/frontend/coprs_frontend/coprs/templates/layout.html
@@ -46,7 +46,7 @@
<a href="https://admin.fedoraproject.org/accounts/user/view/{{ g.user.name }}">{{ g.user.name }}</a> | <a href="{{ url_for('misc.logout') }}">log out</a>
{% else %}
<a href="{{ url_for('misc.login') }}">log in</a> |
- {% for key,val in config.KRB5_LOGIN.iteritems() %}
+ {% for key,val in config.KRB5_LOGIN.items() %}
<a href="{{ config.KRB5_LOGIN_BASEURI }}{{ val.URI }}/">{{ val.log_text }}</a> |
{% endfor %}
<a href="https://admin.fedoraproject.org/accounts/user/new">sign up</a>
diff --git a/frontend/coprs_frontend/tests/test_helpers.py b/frontend/coprs_frontend/tests/test_helpers.py
index 65ffe9e..4ed29a0 100644
--- a/frontend/coprs_frontend/tests/test_helpers.py
+++ b/frontend/coprs_frontend/tests/test_helpers.py
@@ -33,7 +33,7 @@ class TestHelpers(CoprsTestCase):
'xorg-x11-fonts-ISO8859-1-75dpi-7.1-2.1.el5.noarch.rpm': 'xorg-x11-fonts-ISO8859-1-75dpi',
}
- for pkg, expected in EXP.iteritems():
+ for pkg, expected in EXP.items():
assert parse_package_name(pkg) == expected
def test_generate_repo_url(self):
8 years, 7 months
[copr] frontend-python3: [frontend] use python2/3 compatible metaclasses (b46e3ba)
by frostyx@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : frontend-python3
>---------------------------------------------------------------
commit b46e3bad331368caa7cfdaf5dd9dd40721532a04
Author: Jakub Kadlčík <jkadlcik(a)redhat.com>
Date: Tue Sep 22 11:17:59 2015 +0200
[frontend] use python2/3 compatible metaclasses
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/helpers.py | 22 ++++++++--------------
1 files changed, 8 insertions(+), 14 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/helpers.py b/frontend/coprs_frontend/coprs/helpers.py
index e1e9584..a1f3e9a 100644
--- a/frontend/coprs_frontend/coprs/helpers.py
+++ b/frontend/coprs_frontend/coprs/helpers.py
@@ -3,6 +3,7 @@ import random
import string
import flask
+from six import with_metaclass
from six.moves.urllib.parse import urljoin
from dateutil import parser as dt_parser
from netaddr import IPAddress, IPNetwork
@@ -48,8 +49,7 @@ class EnumType(type):
return self.vals[attr]
-class PermissionEnum(object):
- __metaclass__ = EnumType
+class PermissionEnum(with_metaclass(EnumType, object)):
vals = {"nothing": 0, "request": 1, "approved": 2}
@classmethod
@@ -57,8 +57,7 @@ class PermissionEnum(object):
return [(n, k) for k, n in cls.vals.items() if n != without]
-class ActionTypeEnum(object):
- __metaclass__ = EnumType
+class ActionTypeEnum(with_metaclass(EnumType, object)):
vals = {
"delete": 0,
"rename": 1,
@@ -68,18 +67,15 @@ class ActionTypeEnum(object):
}
-class BackendResultEnum(object):
- __metaclass__ = EnumType
+class BackendResultEnum(with_metaclass(EnumType, object)):
vals = {"waiting": 0, "success": 1, "failure": 2}
-class RoleEnum(object):
- __metaclass__ = EnumType
+class RoleEnum(with_metaclass(EnumType, object)):
vals = {"user": 0, "admin": 1}
-class StatusEnum(object):
- __metaclass__ = EnumType
+class StatusEnum(with_metaclass(EnumType, object)):
vals = {"failed": 0,
"succeeded": 1,
"canceled": 2,
@@ -90,15 +86,13 @@ class StatusEnum(object):
"importing": 7} # SRPM is being imported to dist-git
-class BuildSourceEnum(object):
- __metaclass__ = EnumType
+class BuildSourceEnum(with_metaclass(EnumType, object)):
vals = {"unset": 0,
"srpm_link": 1, # url
"srpm_upload": 2} # pkg, tmp
-class FailTypeEnum(object):
- __metaclass__ = EnumType
+class FailTypeEnum(with_metaclass(EnumType, object)):
vals = {"unset": 0,
"unknown_error": 1,
"build_error": 2,
8 years, 7 months
[copr] frontend-python3: [frontend] use rpmutils provided by DNF (9b329da)
by frostyx@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : frontend-python3
>---------------------------------------------------------------
commit 9b329dab89f46995c18b1fd4912d24a6303fd8df
Author: Jakub Kadlčík <jkadlcik(a)redhat.com>
Date: Mon Sep 21 16:37:30 2015 +0200
[frontend] use rpmutils provided by DNF
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/helpers.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/helpers.py b/frontend/coprs_frontend/coprs/helpers.py
index 381fde8..e1e9584 100644
--- a/frontend/coprs_frontend/coprs/helpers.py
+++ b/frontend/coprs_frontend/coprs/helpers.py
@@ -12,7 +12,7 @@ from redis import StrictRedis
from coprs import constants, app
from coprs import app
-from rpmUtils.miscutils import splitFilename
+from dnf.rpm.miscutils import splitFilename
def generate_api_token(size=30):
8 years, 7 months
[copr] frontend-python3: [frontend] fix import path of rest_api (a9a54df)
by frostyx@fedoraproject.org
Repository : http://git.fedorahosted.org/cgit/copr.git
On branch : frontend-python3
>---------------------------------------------------------------
commit a9a54df360d4e57d6e3d4f6c3c3bb3d0fdb8f55c
Author: Jakub Kadlčík <jkadlcik(a)redhat.com>
Date: Tue Sep 22 10:20:45 2015 +0200
[frontend] fix import path of rest_api
>---------------------------------------------------------------
frontend/coprs_frontend/coprs/__init__.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/__init__.py b/frontend/coprs_frontend/coprs/__init__.py
index dd0eb05..483274a 100644
--- a/frontend/coprs_frontend/coprs/__init__.py
+++ b/frontend/coprs_frontend/coprs/__init__.py
@@ -71,7 +71,7 @@ app.register_blueprint(tmp_ns.tmp_ns)
app.add_url_rule("/", "coprs_ns.coprs_show", coprs_general.coprs_show)
-from rest_api import rest_api_bp, register_api_error_handler, URL_PREFIX
+from coprs.rest_api import rest_api_bp, register_api_error_handler, URL_PREFIX
register_api_error_handler(app)
app.register_blueprint(rest_api_bp, url_prefix=URL_PREFIX)
# register_api(app, db)
8 years, 7 months