summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml9
-rw-r--r--docs/conf.py164
-rw-r--r--docs/ext/docstrings.py41
-rw-r--r--gitlab/__init__.py310
-rw-r--r--gitlab/__main__.py2
-rw-r--r--gitlab/base.py70
-rw-r--r--gitlab/cli.py109
-rw-r--r--gitlab/config.py69
-rw-r--r--gitlab/const.py12
-rw-r--r--gitlab/exceptions.py6
-rw-r--r--gitlab/mixins.py112
-rw-r--r--gitlab/tests/test_base.py80
-rw-r--r--gitlab/tests/test_cli.py65
-rw-r--r--gitlab/tests/test_config.py46
-rw-r--r--gitlab/tests/test_gitlab.py444
-rw-r--r--gitlab/tests/test_mixins.py239
-rw-r--r--gitlab/tests/test_types.py30
-rw-r--r--gitlab/types.py4
-rw-r--r--gitlab/utils.py4
-rw-r--r--gitlab/v4/cli.py261
-rw-r--r--gitlab/v4/objects.py2830
-rw-r--r--setup.py68
-rwxr-xr-xtools/ee-test.py87
-rwxr-xr-xtools/generate_token.py10
-rw-r--r--tools/python_test_v4.py851
25 files changed, 3301 insertions, 2622 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0b8fa4f..c50f2aa 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -17,6 +17,15 @@ commitlint:
except:
- master
+black_lint:
+ stage: lint
+ before_script:
+ - pip3 install black
+ script:
+ - black --check .
+ except:
+ - master
+
#build_test_image: # Currently hangs forever, because of GitLab Runner infrastructure issues
# stage: build-test-image
# image:
diff --git a/docs/conf.py b/docs/conf.py
index 4b4a760..a5e5406 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -20,44 +20,42 @@ import sys
import sphinx
-sys.path.append('../')
+sys.path.append("../")
sys.path.append(os.path.dirname(__file__))
import gitlab
-on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+on_rtd = os.environ.get("READTHEDOCS", None) == "True"
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath('..'))
+sys.path.insert(0, os.path.abspath(".."))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-extensions = [
- 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'ext.docstrings'
-]
+extensions = ["sphinx.ext.autodoc", "sphinx.ext.autosummary", "ext.docstrings"]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
# The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# General information about the project.
-project = 'python-gitlab'
-copyright = '2013-2018, Gauvain Pocentek, Mika Mäenpää'
+project = "python-gitlab"
+copyright = "2013-2018, Gauvain Pocentek, Mika Mäenpää"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -70,175 +68,179 @@ release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-#language = None
+# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
-#today = ''
+# today = ''
# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
+exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
-#default_role = None
+# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+# keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
-if not on_rtd: # only import and set the theme if we're building docs locally
+html_theme = "default"
+if not on_rtd: # only import and set the theme if we're building docs locally
try:
import sphinx_rtd_theme
- html_theme = 'sphinx_rtd_theme'
+
+ html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
- except ImportError: # Theme not found, use default
+ except ImportError: # Theme not found, use default
pass
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
-#html_theme_options = {}
+# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
-#html_title = None
+# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-#html_logo = None
+# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-#html_favicon = None
+# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
-#html_extra_path = []
+# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
# If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
# If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
# Output file base name for HTML help builder.
-htmlhelp_basename = 'python-gitlabdoc'
+htmlhelp_basename = "python-gitlabdoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
+ # The paper size ('letterpaper' or 'a4paper').
+ #'papersize': 'letterpaper',
+ # The font size ('10pt', '11pt' or '12pt').
+ #'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ #'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- ('index', 'python-gitlab.tex', 'python-gitlab Documentation',
- 'Gauvain Pocentek, Mika Mäenpää', 'manual'),
+ (
+ "index",
+ "python-gitlab.tex",
+ "python-gitlab Documentation",
+ "Gauvain Pocentek, Mika Mäenpää",
+ "manual",
+ )
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-#latex_logo = None
+# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
# If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
# If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
# Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
# If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
@@ -246,12 +248,17 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('index', 'python-gitlab', 'python-gitlab Documentation',
- ['Gauvain Pocentek, Mika Mäenpää'], 1)
+ (
+ "index",
+ "python-gitlab",
+ "python-gitlab Documentation",
+ ["Gauvain Pocentek, Mika Mäenpää"],
+ 1,
+ )
]
# If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
@@ -260,20 +267,25 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- ('index', 'python-gitlab', 'python-gitlab Documentation',
- 'Gauvain Pocentek, Mika Mäenpää', 'python-gitlab', 'One line description of project.',
- 'Miscellaneous'),
+ (
+ "index",
+ "python-gitlab",
+ "python-gitlab Documentation",
+ "Gauvain Pocentek, Mika Mäenpää",
+ "python-gitlab",
+ "One line description of project.",
+ "Miscellaneous",
+ )
]
# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
# If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
-
+# texinfo_no_detailmenu = False
diff --git a/docs/ext/docstrings.py b/docs/ext/docstrings.py
index 5035f4f..e42bb60 100644
--- a/docs/ext/docstrings.py
+++ b/docs/ext/docstrings.py
@@ -13,46 +13,47 @@ def classref(value, short=True):
return value
if not inspect.isclass(value):
- return ':class:%s' % value
- tilde = '~' if short else ''
- string = '%s.%s' % (value.__module__, value.__name__)
- return ':class:`%sgitlab.objects.%s`' % (tilde, value.__name__)
+ return ":class:%s" % value
+ tilde = "~" if short else ""
+ string = "%s.%s" % (value.__module__, value.__name__)
+ return ":class:`%sgitlab.objects.%s`" % (tilde, value.__name__)
def setup(app):
- app.connect('autodoc-process-docstring', _process_docstring)
- app.connect('autodoc-skip-member', napoleon._skip_member)
+ app.connect("autodoc-process-docstring", _process_docstring)
+ app.connect("autodoc-skip-member", napoleon._skip_member)
conf = napoleon.Config._config_values
for name, (default, rebuild) in six.iteritems(conf):
app.add_config_value(name, default, rebuild)
- return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
+ return {"version": sphinx.__display_version__, "parallel_read_safe": True}
def _process_docstring(app, what, name, obj, options, lines):
result_lines = lines
- docstring = GitlabDocstring(result_lines, app.config, app, what, name, obj,
- options)
+ docstring = GitlabDocstring(result_lines, app.config, app, what, name, obj, options)
result_lines = docstring.lines()
lines[:] = result_lines[:]
class GitlabDocstring(GoogleDocstring):
def _build_doc(self, tmpl, **kwargs):
- env = jinja2.Environment(loader=jinja2.FileSystemLoader(
- os.path.dirname(__file__)), trim_blocks=False)
- env.filters['classref'] = classref
+ env = jinja2.Environment(
+ loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), trim_blocks=False
+ )
+ env.filters["classref"] = classref
template = env.get_template(tmpl)
output = template.render(**kwargs)
- return output.split('\n')
+ return output.split("\n")
- def __init__(self, docstring, config=None, app=None, what='', name='',
- obj=None, options=None):
- super(GitlabDocstring, self).__init__(docstring, config, app, what,
- name, obj, options)
+ def __init__(
+ self, docstring, config=None, app=None, what="", name="", obj=None, options=None
+ ):
+ super(GitlabDocstring, self).__init__(
+ docstring, config, app, what, name, obj, options
+ )
- if name.startswith('gitlab.v4.objects') and name.endswith('Manager'):
- self._parsed_lines.extend(self._build_doc('manager_tmpl.j2',
- cls=self._obj))
+ if name.startswith("gitlab.v4.objects") and name.endswith("Manager"):
+ self._parsed_lines.extend(self._build_doc("manager_tmpl.j2", cls=self._obj))
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
index 9532267..fb21985 100644
--- a/gitlab/__init__.py
+++ b/gitlab/__init__.py
@@ -30,26 +30,26 @@ from gitlab.const import * # noqa
from gitlab.exceptions import * # noqa
from gitlab import utils # noqa
-__title__ = 'python-gitlab'
-__version__ = '1.8.0'
-__author__ = 'Gauvain Pocentek'
-__email__ = 'gauvainpocentek@gmail.com'
-__license__ = 'LGPL3'
-__copyright__ = 'Copyright 2013-2019 Gauvain Pocentek'
+__title__ = "python-gitlab"
+__version__ = "1.8.0"
+__author__ = "Gauvain Pocentek"
+__email__ = "gauvainpocentek@gmail.com"
+__license__ = "LGPL3"
+__copyright__ = "Copyright 2013-2019 Gauvain Pocentek"
-warnings.filterwarnings('default', category=DeprecationWarning,
- module='^gitlab')
+warnings.filterwarnings("default", category=DeprecationWarning, module="^gitlab")
-REDIRECT_MSG = ('python-gitlab detected an http to https redirection. You '
- 'must update your GitLab URL to use https:// to avoid issues.')
+REDIRECT_MSG = (
+ "python-gitlab detected an http to https redirection. You "
+ "must update your GitLab URL to use https:// to avoid issues."
+)
def _sanitize(value):
if isinstance(value, dict):
- return dict((k, _sanitize(v))
- for k, v in six.iteritems(value))
+ return dict((k, _sanitize(v)) for k, v in six.iteritems(value))
if isinstance(value, six.string_types):
- return value.replace('/', '%2F')
+ return value.replace("/", "%2F")
return value
@@ -71,15 +71,26 @@ class Gitlab(object):
api_version (str): Gitlab API version to use (support for 4 only)
"""
- def __init__(self, url, private_token=None, oauth_token=None, email=None,
- password=None, ssl_verify=True, http_username=None,
- http_password=None, timeout=None, api_version='4',
- session=None, per_page=None):
+ def __init__(
+ self,
+ url,
+ private_token=None,
+ oauth_token=None,
+ email=None,
+ password=None,
+ ssl_verify=True,
+ http_username=None,
+ http_password=None,
+ timeout=None,
+ api_version="4",
+ session=None,
+ per_page=None,
+ ):
self._api_version = str(api_version)
self._server_version = self._server_revision = None
self._base_url = url
- self._url = '%s/api/v%s' % (url, api_version)
+ self._url = "%s/api/v%s" % (url, api_version)
#: Timeout to use for requests to gitlab server
self.timeout = timeout
#: Headers that will be used in request to GitLab
@@ -103,8 +114,7 @@ class Gitlab(object):
self.per_page = per_page
- objects = importlib.import_module('gitlab.v%s.objects' %
- self._api_version)
+ objects = importlib.import_module("gitlab.v%s.objects" % self._api_version)
self._objects = objects
self.broadcastmessages = objects.BroadcastMessageManager(self)
@@ -141,13 +151,12 @@ class Gitlab(object):
def __getstate__(self):
state = self.__dict__.copy()
- state.pop('_objects')
+ state.pop("_objects")
return state
def __setstate__(self, state):
self.__dict__.update(state)
- objects = importlib.import_module('gitlab.v%s.objects' %
- self._api_version)
+ objects = importlib.import_module("gitlab.v%s.objects" % self._api_version)
self._objects = objects
@property
@@ -179,15 +188,20 @@ class Gitlab(object):
Raises:
gitlab.config.GitlabDataError: If the configuration is not correct.
"""
- config = gitlab.config.GitlabConfigParser(gitlab_id=gitlab_id,
- config_files=config_files)
- return cls(config.url, private_token=config.private_token,
- oauth_token=config.oauth_token,
- ssl_verify=config.ssl_verify, timeout=config.timeout,
- http_username=config.http_username,
- http_password=config.http_password,
- api_version=config.api_version,
- per_page=config.per_page)
+ config = gitlab.config.GitlabConfigParser(
+ gitlab_id=gitlab_id, config_files=config_files
+ )
+ return cls(
+ config.url,
+ private_token=config.private_token,
+ oauth_token=config.oauth_token,
+ ssl_verify=config.ssl_verify,
+ timeout=config.timeout,
+ http_username=config.http_username,
+ http_password=config.http_password,
+ api_version=config.api_version,
+ per_page=config.per_page,
+ )
def auth(self):
"""Performs an authentication.
@@ -203,8 +217,8 @@ class Gitlab(object):
self._credentials_auth()
def _credentials_auth(self):
- data = {'email': self.email, 'password': self.password}
- r = self.http_post('/session', data)
+ data = {"email": self.email, "password": self.password}
+ r = self.http_post("/session", data)
manager = self._objects.CurrentUserManager(self)
self.user = self._objects.CurrentUser(manager, r)
self.private_token = self.user.private_token
@@ -226,11 +240,11 @@ class Gitlab(object):
"""
if self._server_version is None:
try:
- data = self.http_get('/version')
- self._server_version = data['version']
- self._server_revision = data['revision']
+ data = self.http_get("/version")
+ self._server_version = data["version"]
+ self._server_revision = data["revision"]
except Exception:
- self._server_version = self._server_revision = 'unknown'
+ self._server_version = self._server_revision = "unknown"
return self._server_version, self._server_revision
@@ -250,9 +264,9 @@ class Gitlab(object):
tuple: (True, []) if the file is valid, (False, errors(list))
otherwise
"""
- post_data = {'content': content}
- data = self.http_post('/ci/lint', post_data=post_data, **kwargs)
- return (data['status'] == 'valid', data['errors'])
+ post_data = {"content": content}
+ data = self.http_post("/ci/lint", post_data=post_data, **kwargs)
+ return (data["status"] == "valid", data["errors"])
@on_http_error(GitlabMarkdownError)
def markdown(self, text, gfm=False, project=None, **kwargs):
@@ -273,11 +287,11 @@ class Gitlab(object):
Returns:
str: The HTML rendering of the markdown text.
"""
- post_data = {'text': text, 'gfm': gfm}
+ post_data = {"text": text, "gfm": gfm}
if project is not None:
- post_data['project'] = project
- data = self.http_post('/markdown', post_data=post_data, **kwargs)
- return data['html']
+ post_data["project"] = project
+ data = self.http_post("/markdown", post_data=post_data, **kwargs)
+ return data["html"]
@on_http_error(GitlabLicenseError)
def get_license(self, **kwargs):
@@ -293,7 +307,7 @@ class Gitlab(object):
Returns:
dict: The current license information
"""
- return self.http_get('/license', **kwargs)
+ return self.http_get("/license", **kwargs)
@on_http_error(GitlabLicenseError)
def set_license(self, license, **kwargs):
@@ -310,54 +324,61 @@ class Gitlab(object):
Returns:
dict: The new license information
"""
- data = {'license': license}
- return self.http_post('/license', post_data=data, **kwargs)
+ data = {"license": license}
+ return self.http_post("/license", post_data=data, **kwargs)
def _construct_url(self, id_, obj, parameters, action=None):
- if 'next_url' in parameters:
- return parameters['next_url']
+ if "next_url" in parameters:
+ return parameters["next_url"]
args = _sanitize(parameters)
- url_attr = '_url'
+ url_attr = "_url"
if action is not None:
- attr = '_%s_url' % action
+ attr = "_%s_url" % action
if hasattr(obj, attr):
url_attr = attr
obj_url = getattr(obj, url_attr)
url = obj_url % args
if id_ is not None:
- return '%s/%s' % (url, str(id_))
+ return "%s/%s" % (url, str(id_))
else:
return url
def _set_auth_info(self):
if self.private_token and self.oauth_token:
- raise ValueError("Only one of private_token or oauth_token should "
- "be defined")
- if ((self.http_username and not self.http_password)
- or (not self.http_username and self.http_password)):
- raise ValueError("Both http_username and http_password should "
- "be defined")
+ raise ValueError(
+ "Only one of private_token or oauth_token should " "be defined"
+ )
+ if (self.http_username and not self.http_password) or (
+ not self.http_username and self.http_password
+ ):
+ raise ValueError(
+ "Both http_username and http_password should " "be defined"
+ )
if self.oauth_token and self.http_username:
- raise ValueError("Only one of oauth authentication or http "
- "authentication should be defined")
+ raise ValueError(
+ "Only one of oauth authentication or http "
+ "authentication should be defined"
+ )
self._http_auth = None
if self.private_token:
- self.headers['PRIVATE-TOKEN'] = self.private_token
- self.headers.pop('Authorization', None)
+ self.headers["PRIVATE-TOKEN"] = self.private_token
+ self.headers.pop("Authorization", None)
if self.oauth_token:
- self.headers['Authorization'] = "Bearer %s" % self.oauth_token
- self.headers.pop('PRIVATE-TOKEN', None)
+ self.headers["Authorization"] = "Bearer %s" % self.oauth_token
+ self.headers.pop("PRIVATE-TOKEN", None)
if self.http_username:
- self._http_auth = requests.auth.HTTPBasicAuth(self.http_username,
- self.http_password)
+ self._http_auth = requests.auth.HTTPBasicAuth(
+ self.http_username, self.http_password
+ )
def enable_debug(self):
import logging
+
try:
from http.client import HTTPConnection # noqa
except ImportError:
@@ -373,15 +394,15 @@ class Gitlab(object):
def _create_headers(self, content_type=None):
request_headers = self.headers.copy()
if content_type is not None:
- request_headers['Content-type'] = content_type
+ request_headers["Content-type"] = content_type
return request_headers
def _get_session_opts(self, content_type):
return {
- 'headers': self._create_headers(content_type),
- 'auth': self._http_auth,
- 'timeout': self.timeout,
- 'verify': self.ssl_verify
+ "headers": self._create_headers(content_type),
+ "auth": self._http_auth,
+ "timeout": self.timeout,
+ "verify": self.ssl_verify,
}
def _build_url(self, path):
@@ -393,10 +414,10 @@ class Gitlab(object):
Returns:
str: The full URL
"""
- if path.startswith('http://') or path.startswith('https://'):
+ if path.startswith("http://") or path.startswith("https://"):
return path
else:
- return '%s%s' % (self._url, path)
+ return "%s%s" % (self._url, path)
def _check_redirects(self, result):
# Check the requests history to detect http to https redirections.
@@ -406,20 +427,28 @@ class Gitlab(object):
# request.
# If we detect a redirection to https with a POST or a PUT request, we
# raise an exception with a useful error message.
- if result.history and self._base_url.startswith('http:'):
+ if result.history and self._base_url.startswith("http:"):
for item in result.history:
if item.status_code not in (301, 302):
continue
# GET methods can be redirected without issue
- if item.request.method == 'GET':
+ if item.request.method == "GET":
continue
# Did we end-up with an https:// URL?
- location = item.headers.get('Location', None)
- if location and location.startswith('https://'):
+ location = item.headers.get("Location", None)
+ if location and location.startswith("https://"):
raise RedirectError(REDIRECT_MSG)
- def http_request(self, verb, path, query_data={}, post_data=None,
- streamed=False, files=None, **kwargs):
+ def http_request(
+ self,
+ verb,
+ path,
+ query_data={},
+ post_data=None,
+ streamed=False,
+ files=None,
+ **kwargs
+ ):
"""Make an HTTP request to the Gitlab server.
Args:
@@ -452,18 +481,18 @@ class Gitlab(object):
# So we provide a `query_parameters` key: if it's there we use its dict
# value as arguments for the gitlab server, and ignore the other
# arguments, except pagination ones (per_page and page)
- if 'query_parameters' in kwargs:
- utils.copy_dict(params, kwargs['query_parameters'])
- for arg in ('per_page', 'page'):
+ if "query_parameters" in kwargs:
+ utils.copy_dict(params, kwargs["query_parameters"])
+ for arg in ("per_page", "page"):
if arg in kwargs:
params[arg] = kwargs[arg]
else:
utils.copy_dict(params, kwargs)
- opts = self._get_session_opts(content_type='application/json')
+ opts = self._get_session_opts(content_type="application/json")
- verify = opts.pop('verify')
- timeout = opts.pop('timeout')
+ verify = opts.pop("verify")
+ timeout = opts.pop("timeout")
# We need to deal with json vs. data when uploading files
if files:
@@ -480,12 +509,14 @@ class Gitlab(object):
# The Requests behavior is right but it seems that web servers don't
# always agree with this decision (this is the case with a default
# gitlab installation)
- req = requests.Request(verb, url, json=json, data=data, params=params,
- files=files, **opts)
+ req = requests.Request(
+ verb, url, json=json, data=data, params=params, files=files, **opts
+ )
prepped = self.session.prepare_request(req)
prepped.url = utils.sanitized_url(prepped.url)
settings = self.session.merge_environment_settings(
- prepped.url, {}, streamed, verify, None)
+ prepped.url, {}, streamed, verify, None
+ )
# obey the rate limit by default
obey_rate_limit = kwargs.get("obey_rate_limit", True)
@@ -514,7 +545,7 @@ class Gitlab(object):
error_message = result.content
try:
error_json = result.json()
- for k in ('message', 'error'):
+ for k in ("message", "error"):
if k in error_json:
error_message = error_json[k]
except (KeyError, ValueError, TypeError):
@@ -524,14 +555,16 @@ class Gitlab(object):
raise GitlabAuthenticationError(
response_code=result.status_code,
error_message=error_message,
- response_body=result.content)
+ response_body=result.content,
+ )
- raise GitlabHttpError(response_code=result.status_code,
- error_message=error_message,
- response_body=result.content)
+ raise GitlabHttpError(
+ response_code=result.status_code,
+ error_message=error_message,
+ response_body=result.content,
+ )
- def http_get(self, path, query_data={}, streamed=False, raw=False,
- **kwargs):
+ def http_get(self, path, query_data={}, streamed=False, raw=False, **kwargs):
"""Make a GET request to the Gitlab server.
Args:
@@ -551,17 +584,21 @@ class Gitlab(object):
GitlabHttpError: When the return code is not 2xx
GitlabParsingError: If the json data could not be parsed
"""
- result = self.http_request('get', path, query_data=query_data,
- streamed=streamed, **kwargs)
-
- if (result.headers['Content-Type'] == 'application/json'
- and not streamed
- and not raw):
+ result = self.http_request(
+ "get", path, query_data=query_data, streamed=streamed, **kwargs
+ )
+
+ if (
+ result.headers["Content-Type"] == "application/json"
+ and not streamed
+ and not raw
+ ):
try:
return result.json()
except Exception:
raise GitlabParsingError(
- error_message="Failed to parse the server message")
+ error_message="Failed to parse the server message"
+ )
else:
return result
@@ -590,22 +627,20 @@ class Gitlab(object):
# In case we want to change the default behavior at some point
as_list = True if as_list is None else as_list
- get_all = kwargs.pop('all', False)
+ get_all = kwargs.pop("all", False)
url = self._build_url(path)
if get_all is True:
return list(GitlabList(self, url, query_data, **kwargs))
- if 'page' in kwargs or as_list is True:
+ if "page" in kwargs or as_list is True:
# pagination requested, we return a list
- return list(GitlabList(self, url, query_data, get_next=False,
- **kwargs))
+ return list(GitlabList(self, url, query_data, get_next=False, **kwargs))
# No pagination, generator requested
return GitlabList(self, url, query_data, **kwargs)
- def http_post(self, path, query_data={}, post_data={}, files=None,
- **kwargs):
+ def http_post(self, path, query_data={}, post_data={}, files=None, **kwargs):
"""Make a POST request to the Gitlab server.
Args:
@@ -625,18 +660,22 @@ class Gitlab(object):
GitlabHttpError: When the return code is not 2xx
GitlabParsingError: If the json data could not be parsed
"""
- result = self.http_request('post', path, query_data=query_data,
- post_data=post_data, files=files, **kwargs)
+ result = self.http_request(
+ "post",
+ path,
+ query_data=query_data,
+ post_data=post_data,
+ files=files,
+ **kwargs
+ )
try:
- if result.headers.get('Content-Type', None) == 'application/json':
+ if result.headers.get("Content-Type", None) == "application/json":
return result.json()
except Exception:
- raise GitlabParsingError(
- error_message="Failed to parse the server message")
+ raise GitlabParsingError(error_message="Failed to parse the server message")
return result
- def http_put(self, path, query_data={}, post_data={}, files=None,
- **kwargs):
+ def http_put(self, path, query_data={}, post_data={}, files=None, **kwargs):
"""Make a PUT request to the Gitlab server.
Args:
@@ -655,13 +694,18 @@ class Gitlab(object):
GitlabHttpError: When the return code is not 2xx
GitlabParsingError: If the json data could not be parsed
"""
- result = self.http_request('put', path, query_data=query_data,
- post_data=post_data, files=files, **kwargs)
+ result = self.http_request(
+ "put",
+ path,
+ query_data=query_data,
+ post_data=post_data,
+ files=files,
+ **kwargs
+ )
try:
return result.json()
except Exception:
- raise GitlabParsingError(
- error_message="Failed to parse the server message")
+ raise GitlabParsingError(error_message="Failed to parse the server message")
def http_delete(self, path, **kwargs):
"""Make a PUT request to the Gitlab server.
@@ -677,7 +721,7 @@ class Gitlab(object):
Raises:
GitlabHttpError: When the return code is not 2xx
"""
- return self.http_request('delete', path, **kwargs)
+ return self.http_request("delete", path, **kwargs)
@on_http_error(GitlabSearchError)
def search(self, scope, search, **kwargs):
@@ -695,8 +739,8 @@ class Gitlab(object):
Returns:
GitlabList: A list of dicts describing the resources found.
"""
- data = {'scope': scope, 'search': search}
- return self.http_list('/search', query_data=data, **kwargs)
+ data = {"scope": scope, "search": search}
+ return self.http_list("/search", query_data=data, **kwargs)
class GitlabList(object):
@@ -712,24 +756,22 @@ class GitlabList(object):
self._get_next = get_next
def _query(self, url, query_data={}, **kwargs):
- result = self._gl.http_request('get', url, query_data=query_data,
- **kwargs)
+ result = self._gl.http_request("get", url, query_data=query_data, **kwargs)
try:
- self._next_url = result.links['next']['url']
+ self._next_url = result.links["next"]["url"]
except KeyError:
self._next_url = None
- self._current_page = result.headers.get('X-Page')
- self._prev_page = result.headers.get('X-Prev-Page')
- self._next_page = result.headers.get('X-Next-Page')
- self._per_page = result.headers.get('X-Per-Page')
- self._total_pages = result.headers.get('X-Total-Pages')
- self._total = result.headers.get('X-Total')
+ self._current_page = result.headers.get("X-Page")
+ self._prev_page = result.headers.get("X-Prev-Page")
+ self._next_page = result.headers.get("X-Next-Page")
+ self._per_page = result.headers.get("X-Per-Page")
+ self._total_pages = result.headers.get("X-Total-Pages")
+ self._total = result.headers.get("X-Total")
try:
self._data = result.json()
except Exception:
- raise GitlabParsingError(
- error_message="Failed to parse the server message")
+ raise GitlabParsingError(error_message="Failed to parse the server message")
self._current = 0
diff --git a/gitlab/__main__.py b/gitlab/__main__.py
index 7d8d087..14a1fa2 100644
--- a/gitlab/__main__.py
+++ b/gitlab/__main__.py
@@ -1,4 +1,4 @@
import gitlab.cli
-__name__ == '__main__' and gitlab.cli.main()
+__name__ == "__main__" and gitlab.cli.main()
diff --git a/gitlab/base.py b/gitlab/base.py
index 7a88881..d2e44b8 100644
--- a/gitlab/base.py
+++ b/gitlab/base.py
@@ -28,35 +28,38 @@ class RESTObject(object):
must be used as uniq ID. ``None`` means that the object can be updated
without ID in the url.
"""
- _id_attr = 'id'
+
+ _id_attr = "id"
def __init__(self, manager, attrs):
- self.__dict__.update({
- 'manager': manager,
- '_attrs': attrs,
- '_updated_attrs': {},
- '_module': importlib.import_module(self.__module__)
- })
- self.__dict__['_parent_attrs'] = self.manager.parent_attrs
+ self.__dict__.update(
+ {
+ "manager": manager,
+ "_attrs": attrs,
+ "_updated_attrs": {},
+ "_module": importlib.import_module(self.__module__),
+ }
+ )
+ self.__dict__["_parent_attrs"] = self.manager.parent_attrs
self._create_managers()
def __getstate__(self):
state = self.__dict__.copy()
- module = state.pop('_module')
- state['_module_name'] = module.__name__
+ module = state.pop("_module")
+ state["_module_name"] = module.__name__
return state
def __setstate__(self, state):
- module_name = state.pop('_module_name')
+ module_name = state.pop("_module_name")
self.__dict__.update(state)
self._module = importlib.import_module(module_name)
def __getattr__(self, name):
try:
- return self.__dict__['_updated_attrs'][name]
+ return self.__dict__["_updated_attrs"][name]
except KeyError:
try:
- value = self.__dict__['_attrs'][name]
+ value = self.__dict__["_attrs"][name]
# If the value is a list, we copy it in the _updated_attrs dict
# because we are not able to detect changes made on the object
@@ -69,32 +72,34 @@ class RESTObject(object):
# note: _parent_attrs will only store simple values (int) so we
# don't make this check in the next except block.
if isinstance(value, list):
- self.__dict__['_updated_attrs'][name] = value[:]
- return self.__dict__['_updated_attrs'][name]
+ self.__dict__["_updated_attrs"][name] = value[:]
+ return self.__dict__["_updated_attrs"][name]
return value
except KeyError:
try:
- return self.__dict__['_parent_attrs'][name]
+ return self.__dict__["_parent_attrs"][name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
- self.__dict__['_updated_attrs'][name] = value
+ self.__dict__["_updated_attrs"][name] = value
def __str__(self):
data = self._attrs.copy()
data.update(self._updated_attrs)
- return '%s => %s' % (type(self), data)
+ return "%s => %s" % (type(self), data)
def __repr__(self):
if self._id_attr:
- return '<%s %s:%s>' % (self.__class__.__name__,
- self._id_attr,
- self.get_id())
+ return "<%s %s:%s>" % (
+ self.__class__.__name__,
+ self._id_attr,
+ self.get_id(),
+ )
else:
- return '<%s>' % self.__class__.__name__
+ return "<%s>" % self.__class__.__name__
def __eq__(self, other):
if self.get_id() and other.get_id():
@@ -112,7 +117,7 @@ class RESTObject(object):
return hash(self.get_id())
def _create_managers(self):
- managers = getattr(self, '_managers', None)
+ managers = getattr(self, "_managers", None)
if managers is None:
return
@@ -122,8 +127,8 @@ class RESTObject(object):
self.__dict__[attr] = manager
def _update_attrs(self, new_attrs):
- self.__dict__['_updated_attrs'] = {}
- self.__dict__['_attrs'].update(new_attrs)
+ self.__dict__["_updated_attrs"] = {}
+ self.__dict__["_attrs"].update(new_attrs)
def get_id(self):
"""Returns the id of the resource."""
@@ -133,9 +138,9 @@ class RESTObject(object):
@property
def attributes(self):
- d = self.__dict__['_updated_attrs'].copy()
- d.update(self.__dict__['_attrs'])
- d.update(self.__dict__['_parent_attrs'])
+ d = self.__dict__["_updated_attrs"].copy()
+ d.update(self.__dict__["_attrs"])
+ d.update(self.__dict__["_parent_attrs"])
return d
@@ -153,6 +158,7 @@ class RESTObjectList(object):
obj_cls: Type of objects to create from the json data
_list: A GitlabList object
"""
+
def __init__(self, manager, obj_cls, _list):
"""Creates an objects list from a GitlabList.
@@ -250,11 +256,13 @@ class RESTManager(object):
self._parent_attrs = {}
if path is None:
path = self._path
- if self._parent is None or not hasattr(self, '_from_parent_attrs'):
+ if self._parent is None or not hasattr(self, "_from_parent_attrs"):
return path
- data = {self_attr: getattr(self._parent, parent_attr, None)
- for self_attr, parent_attr in self._from_parent_attrs.items()}
+ data = {
+ self_attr: getattr(self._parent, parent_attr, None)
+ for self_attr, parent_attr in self._from_parent_attrs.items()
+ }
self._parent_attrs = data
return path % data
diff --git a/gitlab/cli.py b/gitlab/cli.py
index b573c7f..0433a81 100644
--- a/gitlab/cli.py
+++ b/gitlab/cli.py
@@ -26,7 +26,7 @@ import sys
import gitlab.config
-camel_re = re.compile('(.)([A-Z])')
+camel_re = re.compile("(.)([A-Z])")
# custom_actions = {
# cls: {
@@ -46,20 +46,21 @@ def register_custom_action(cls_names, mandatory=tuple(), optional=tuple()):
in_obj = True
classes = cls_names
if type(cls_names) != tuple:
- classes = (cls_names, )
+ classes = (cls_names,)
for cls_name in classes:
final_name = cls_name
- if cls_name.endswith('Manager'):
- final_name = cls_name.replace('Manager', '')
+ if cls_name.endswith("Manager"):
+ final_name = cls_name.replace("Manager", "")
in_obj = False
if final_name not in custom_actions:
custom_actions[final_name] = {}
- action = f.__name__.replace('_', '-')
+ action = f.__name__.replace("_", "-")
custom_actions[final_name][action] = (mandatory, optional, in_obj)
return wrapped_f
+
return wrap
@@ -75,38 +76,57 @@ def what_to_cls(what):
def cls_to_what(cls):
- return camel_re.sub(r'\1-\2', cls.__name__).lower()
+ return camel_re.sub(r"\1-\2", cls.__name__).lower()
def _get_base_parser(add_help=True):
parser = argparse.ArgumentParser(
- add_help=add_help,
- description="GitLab API Command Line Interface")
- parser.add_argument("--version", help="Display the version.",
- action="store_true")
- parser.add_argument("-v", "--verbose", "--fancy",
- help="Verbose mode (legacy format only)",
- action="store_true")
- parser.add_argument("-d", "--debug",
- help="Debug mode (display HTTP requests)",
- action="store_true")
- parser.add_argument("-c", "--config-file", action='append',
- help=("Configuration file to use. Can be used "
- "multiple times."))
- parser.add_argument("-g", "--gitlab",
- help=("Which configuration section should "
- "be used. If not defined, the default selection "
- "will be used."),
- required=False)
- parser.add_argument("-o", "--output",
- help="Output format (v4 only): json|legacy|yaml",
- required=False,
- choices=['json', 'legacy', 'yaml'],
- default="legacy")
- parser.add_argument("-f", "--fields",
- help=("Fields to display in the output (comma "
- "separated). Not used with legacy output"),
- required=False)
+ add_help=add_help, description="GitLab API Command Line Interface"
+ )
+ parser.add_argument("--version", help="Display the version.", action="store_true")
+ parser.add_argument(
+ "-v",
+ "--verbose",
+ "--fancy",
+ help="Verbose mode (legacy format only)",
+ action="store_true",
+ )
+ parser.add_argument(
+ "-d", "--debug", help="Debug mode (display HTTP requests)", action="store_true"
+ )
+ parser.add_argument(
+ "-c",
+ "--config-file",
+ action="append",
+ help=("Configuration file to use. Can be used " "multiple times."),
+ )
+ parser.add_argument(
+ "-g",
+ "--gitlab",
+ help=(
+ "Which configuration section should "
+ "be used. If not defined, the default selection "
+ "will be used."
+ ),
+ required=False,
+ )
+ parser.add_argument(
+ "-o",
+ "--output",
+ help="Output format (v4 only): json|legacy|yaml",
+ required=False,
+ choices=["json", "legacy", "yaml"],
+ default="legacy",
+ )
+ parser.add_argument(
+ "-f",
+ "--fields",
+ help=(
+ "Fields to display in the output (comma "
+ "separated). Not used with legacy output"
+ ),
+ required=False,
+ )
return parser
@@ -117,7 +137,7 @@ def _get_parser(cli_module):
def _parse_value(v):
- if isinstance(v, str) and v.startswith('@'):
+ if isinstance(v, str) and v.startswith("@"):
# If the user-provided value starts with @, we try to read the file
# path provided after @ as the real value. Exit on any error.
try:
@@ -142,16 +162,13 @@ def main():
# any subparser setup
(options, args) = parser.parse_known_args(sys.argv)
try:
- config = gitlab.config.GitlabConfigParser(
- options.gitlab,
- options.config_file
- )
+ config = gitlab.config.GitlabConfigParser(options.gitlab, options.config_file)
except gitlab.config.ConfigError as e:
if "--help" in sys.argv or "-h" in sys.argv:
parser.print_help()
sys.exit(0)
sys.exit(e)
- cli_module = importlib.import_module('gitlab.v%s.cli' % config.api_version)
+ cli_module = importlib.import_module("gitlab.v%s.cli" % config.api_version)
# Now we build the entire set of subcommands and do the complete parsing
parser = _get_parser(cli_module)
@@ -163,15 +180,23 @@ def main():
output = args.output
fields = []
if args.fields:
- fields = [x.strip() for x in args.fields.split(',')]
+ fields = [x.strip() for x in args.fields.split(",")]
debug = args.debug
action = args.action
what = args.what
args = args.__dict__
# Remove CLI behavior-related args
- for item in ('gitlab', 'config_file', 'verbose', 'debug', 'what', 'action',
- 'version', 'output'):
+ for item in (
+ "gitlab",
+ "config_file",
+ "verbose",
+ "debug",
+ "what",
+ "action",
+ "version",
+ "output",
+ ):
args.pop(item)
args = {k: _parse_value(v) for k, v in args.items() if v is not None}
diff --git a/gitlab/config.py b/gitlab/config.py
index 1c76594..0c3cff7 100644
--- a/gitlab/config.py
+++ b/gitlab/config.py
@@ -19,10 +19,7 @@ import os
from six.moves import configparser
-_DEFAULT_FILES = [
- '/etc/python-gitlab.cfg',
- os.path.expanduser('~/.python-gitlab.cfg')
-]
+_DEFAULT_FILES = ["/etc/python-gitlab.cfg", os.path.expanduser("~/.python-gitlab.cfg")]
class ConfigError(Exception):
@@ -63,40 +60,41 @@ class GitlabConfigParser(object):
if self.gitlab_id is None:
try:
- self.gitlab_id = self._config.get('global', 'default')
+ self.gitlab_id = self._config.get("global", "default")
except Exception:
- raise GitlabIDError("Impossible to get the gitlab id "
- "(not specified in config file)")
+ raise GitlabIDError(
+ "Impossible to get the gitlab id " "(not specified in config file)"
+ )
try:
- self.url = self._config.get(self.gitlab_id, 'url')
+ self.url = self._config.get(self.gitlab_id, "url")
except Exception:
- raise GitlabDataError("Impossible to get gitlab informations from "
- "configuration (%s)" % self.gitlab_id)
+ raise GitlabDataError(
+ "Impossible to get gitlab informations from "
+ "configuration (%s)" % self.gitlab_id
+ )
self.ssl_verify = True
try:
- self.ssl_verify = self._config.getboolean('global', 'ssl_verify')
+ self.ssl_verify = self._config.getboolean("global", "ssl_verify")
except ValueError:
# Value Error means the option exists but isn't a boolean.
# Get as a string instead as it should then be a local path to a
# CA bundle.
try:
- self.ssl_verify = self._config.get('global', 'ssl_verify')
+ self.ssl_verify = self._config.get("global", "ssl_verify")
except Exception:
pass
except Exception:
pass
try:
- self.ssl_verify = self._config.getboolean(self.gitlab_id,
- 'ssl_verify')
+ self.ssl_verify = self._config.getboolean(self.gitlab_id, "ssl_verify")
except ValueError:
# Value Error means the option exists but isn't a boolean.
# Get as a string instead as it should then be a local path to a
# CA bundle.
try:
- self.ssl_verify = self._config.get(self.gitlab_id,
- 'ssl_verify')
+ self.ssl_verify = self._config.get(self.gitlab_id, "ssl_verify")
except Exception:
pass
except Exception:
@@ -104,66 +102,59 @@ class GitlabConfigParser(object):
self.timeout = 60
try:
- self.timeout = self._config.getint('global', 'timeout')
+ self.timeout = self._config.getint("global", "timeout")
except Exception:
pass
try:
- self.timeout = self._config.getint(self.gitlab_id, 'timeout')
+ self.timeout = self._config.getint(self.gitlab_id, "timeout")
except Exception:
pass
self.private_token = None
try:
- self.private_token = self._config.get(self.gitlab_id,
- 'private_token')
+ self.private_token = self._config.get(self.gitlab_id, "private_token")
except Exception:
pass
self.oauth_token = None
try:
- self.oauth_token = self._config.get(self.gitlab_id, 'oauth_token')
+ self.oauth_token = self._config.get(self.gitlab_id, "oauth_token")
except Exception:
pass
self.http_username = None
self.http_password = None
try:
- self.http_username = self._config.get(self.gitlab_id,
- 'http_username')
- self.http_password = self._config.get(self.gitlab_id,
- 'http_password')
+ self.http_username = self._config.get(self.gitlab_id, "http_username")
+ self.http_password = self._config.get(self.gitlab_id, "http_password")
except Exception:
pass
self.http_username = None
self.http_password = None
try:
- self.http_username = self._config.get(self.gitlab_id,
- 'http_username')
- self.http_password = self._config.get(self.gitlab_id,
- 'http_password')
+ self.http_username = self._config.get(self.gitlab_id, "http_username")
+ self.http_password = self._config.get(self.gitlab_id, "http_password")
except Exception:
pass
- self.api_version = '4'
+ self.api_version = "4"
try:
- self.api_version = self._config.get('global', 'api_version')
+ self.api_version = self._config.get("global", "api_version")
except Exception:
pass
try:
- self.api_version = self._config.get(self.gitlab_id, 'api_version')
+ self.api_version = self._config.get(self.gitlab_id, "api_version")
except Exception:
pass
- if self.api_version not in ('4',):
- raise GitlabDataError("Unsupported API version: %s" %
- self.api_version)
+ if self.api_version not in ("4",):
+ raise GitlabDataError("Unsupported API version: %s" % self.api_version)
self.per_page = None
- for section in ['global', self.gitlab_id]:
+ for section in ["global", self.gitlab_id]:
try:
- self.per_page = self._config.getint(section, 'per_page')
+ self.per_page = self._config.getint(section, "per_page")
except Exception:
pass
if self.per_page is not None and not 0 <= self.per_page <= 100:
- raise GitlabDataError("Unsupported per_page number: %s" %
- self.per_page)
+ raise GitlabDataError("Unsupported per_page number: %s" % self.per_page)
diff --git a/gitlab/const.py b/gitlab/const.py
index 62f2403..aef4a40 100644
--- a/gitlab/const.py
+++ b/gitlab/const.py
@@ -26,9 +26,9 @@ VISIBILITY_PRIVATE = 0
VISIBILITY_INTERNAL = 10
VISIBILITY_PUBLIC = 20
-NOTIFICATION_LEVEL_DISABLED = 'disabled'
-NOTIFICATION_LEVEL_PARTICIPATING = 'participating'
-NOTIFICATION_LEVEL_WATCH = 'watch'
-NOTIFICATION_LEVEL_GLOBAL = 'global'
-NOTIFICATION_LEVEL_MENTION = 'mention'
-NOTIFICATION_LEVEL_CUSTOM = 'custom'
+NOTIFICATION_LEVEL_DISABLED = "disabled"
+NOTIFICATION_LEVEL_PARTICIPATING = "participating"
+NOTIFICATION_LEVEL_WATCH = "watch"
+NOTIFICATION_LEVEL_GLOBAL = "global"
+NOTIFICATION_LEVEL_MENTION = "mention"
+NOTIFICATION_LEVEL_CUSTOM = "custom"
diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py
index 5b7b75c..449b6f0 100644
--- a/gitlab/exceptions.py
+++ b/gitlab/exceptions.py
@@ -19,8 +19,7 @@ import functools
class GitlabError(Exception):
- def __init__(self, error_message="", response_code=None,
- response_body=None):
+ def __init__(self, error_message="", response_code=None, response_body=None):
Exception.__init__(self, error_message)
# Http status code
@@ -248,6 +247,7 @@ def on_http_error(error):
error(Exception): The exception type to raise -- must inherit from
GitlabError
"""
+
def wrap(f):
@functools.wraps(f)
def wrapped_f(*args, **kwargs):
@@ -255,5 +255,7 @@ def on_http_error(error):
return f(*args, **kwargs)
except GitlabHttpError as e:
raise error(e.error_message, e.response_code, e.response_body)
+
return wrapped_f
+
return wrap
diff --git a/gitlab/mixins.py b/gitlab/mixins.py
index ca68658..70de992 100644
--- a/gitlab/mixins.py
+++ b/gitlab/mixins.py
@@ -42,8 +42,8 @@ class GetMixin(object):
GitlabGetError: If the server cannot perform the request
"""
if not isinstance(id, int):
- id = id.replace('/', '%2F')
- path = '%s/%s' % (self.path, id)
+ id = id.replace("/", "%2F")
+ path = "%s/%s" % (self.path, id)
if lazy is True:
return self._obj_cls(self, {self._obj_cls._id_attr: id})
server_data = self.gitlab.http_get(path, **kwargs)
@@ -86,7 +86,7 @@ class RefreshMixin(object):
GitlabGetError: If the server cannot perform the request
"""
if self._id_attr:
- path = '%s/%s' % (self.manager.path, self.id)
+ path = "%s/%s" % (self.manager.path, self.id)
else:
path = self.manager.path
server_data = self.manager.gitlab.http_get(path, **kwargs)
@@ -117,10 +117,10 @@ class ListMixin(object):
# Duplicate data to avoid messing with what the user sent us
data = kwargs.copy()
if self.gitlab.per_page:
- data.setdefault('per_page', self.gitlab.per_page)
+ data.setdefault("per_page", self.gitlab.per_page)
# We get the attributes that need some special transformation
- types = getattr(self, '_types', {})
+ types = getattr(self, "_types", {})
if types:
for attr_name, type_cls in types.items():
if attr_name in data.keys():
@@ -128,7 +128,7 @@ class ListMixin(object):
data[attr_name] = type_obj.get_for_api()
# Allow to overwrite the path, handy for custom listings
- path = data.pop('path', self.path)
+ path = data.pop("path", self.path)
obj = self.gitlab.http_list(path, **data)
if isinstance(obj, list):
@@ -159,7 +159,7 @@ class CreateMixin(object):
tuple: 2 items: list of required arguments and list of optional
arguments for creation (in that order)
"""
- return getattr(self, '_create_attrs', (tuple(), tuple()))
+ return getattr(self, "_create_attrs", (tuple(), tuple()))
@exc.on_http_error(exc.GitlabCreateError)
def create(self, data, **kwargs):
@@ -182,7 +182,7 @@ class CreateMixin(object):
files = {}
# We get the attributes that need some special transformation
- types = getattr(self, '_types', {})
+ types = getattr(self, "_types", {})
if types:
# Duplicate data to avoid messing with what the user sent us
data = data.copy()
@@ -199,9 +199,8 @@ class CreateMixin(object):
data[attr_name] = type_obj.get_for_api()
# Handle specific URL for creation
- path = kwargs.pop('path', self.path)
- server_data = self.gitlab.http_post(path, post_data=data, files=files,
- **kwargs)
+ path = kwargs.pop("path", self.path)
+ server_data = self.gitlab.http_post(path, post_data=data, files=files, **kwargs)
return self._obj_cls(self, server_data)
@@ -223,7 +222,7 @@ class UpdateMixin(object):
tuple: 2 items: list of required arguments and list of optional
arguments for update (in that order)
"""
- return getattr(self, '_update_attrs', (tuple(), tuple()))
+ return getattr(self, "_update_attrs", (tuple(), tuple()))
def _get_update_method(self):
"""Return the HTTP method to use.
@@ -231,7 +230,7 @@ class UpdateMixin(object):
Returns:
object: http_put (default) or http_post
"""
- if getattr(self, '_update_uses_post', False):
+ if getattr(self, "_update_uses_post", False):
http_method = self.gitlab.http_post
else:
http_method = self.gitlab.http_put
@@ -257,13 +256,13 @@ class UpdateMixin(object):
if id is None:
path = self.path
else:
- path = '%s/%s' % (self.path, id)
+ path = "%s/%s" % (self.path, id)
self._check_missing_update_attrs(new_data)
files = {}
# We get the attributes that need some special transformation
- types = getattr(self, '_types', {})
+ types = getattr(self, "_types", {})
if types:
# Duplicate data to avoid messing with what the user sent us
new_data = new_data.copy()
@@ -300,8 +299,8 @@ class SetMixin(object):
Returns:
obj: The created/updated attribute
"""
- path = '%s/%s' % (self.path, key.replace('/', '%2F'))
- data = {'value': value}
+ path = "%s/%s" % (self.path, key.replace("/", "%2F"))
+ data = {"value": value}
server_data = self.gitlab.http_put(path, post_data=data, **kwargs)
return self._obj_cls(self, server_data)
@@ -323,8 +322,8 @@ class DeleteMixin(object):
path = self.path
else:
if not isinstance(id, int):
- id = id.replace('/', '%2F')
- path = '%s/%s' % (self.path, id)
+ id = id.replace("/", "%2F")
+ path = "%s/%s" % (self.path, id)
self.gitlab.http_delete(path, **kwargs)
@@ -338,6 +337,7 @@ class NoUpdateMixin(GetMixin, ListMixin, CreateMixin, DeleteMixin):
class SaveMixin(object):
"""Mixin for RESTObject's that can be updated."""
+
def _get_updated_data(self):
updated_data = {}
required, optional = self.manager.get_update_attrs()
@@ -375,6 +375,7 @@ class SaveMixin(object):
class ObjectDeleteMixin(object):
"""Mixin for RESTObject's that can be deleted."""
+
def delete(self, **kwargs):
"""Delete the object from the server.
@@ -389,7 +390,7 @@ class ObjectDeleteMixin(object):
class UserAgentDetailMixin(object):
- @cli.register_custom_action(('Snippet', 'ProjectSnippet', 'ProjectIssue'))
+ @cli.register_custom_action(("Snippet", "ProjectSnippet", "ProjectIssue"))
@exc.on_http_error(exc.GitlabGetError)
def user_agent_detail(self, **kwargs):
"""Get the user agent detail.
@@ -401,13 +402,14 @@ class UserAgentDetailMixin(object):
GitlabAuthenticationError: If authentication is not correct
GitlabGetError: If the server cannot perform the request
"""
- path = '%s/%s/user_agent_detail' % (self.manager.path, self.get_id())
+ path = "%s/%s/user_agent_detail" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)
class AccessRequestMixin(object):
- @cli.register_custom_action(('ProjectAccessRequest', 'GroupAccessRequest'),
- tuple(), ('access_level', ))
+ @cli.register_custom_action(
+ ("ProjectAccessRequest", "GroupAccessRequest"), tuple(), ("access_level",)
+ )
@exc.on_http_error(exc.GitlabUpdateError)
def approve(self, access_level=gitlab.DEVELOPER_ACCESS, **kwargs):
"""Approve an access request.
@@ -421,16 +423,14 @@ class AccessRequestMixin(object):
GitlabUpdateError: If the server fails to perform the request
"""
- path = '%s/%s/approve' % (self.manager.path, self.id)
- data = {'access_level': access_level}
- server_data = self.manager.gitlab.http_put(path, post_data=data,
- **kwargs)
+ path = "%s/%s/approve" % (self.manager.path, self.id)
+ data = {"access_level": access_level}
+ server_data = self.manager.gitlab.http_put(path, post_data=data, **kwargs)
self._update_attrs(server_data)
class SubscribableMixin(object):
- @cli.register_custom_action(('ProjectIssue', 'ProjectMergeRequest',
- 'ProjectLabel'))
+ @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest", "ProjectLabel"))
@exc.on_http_error(exc.GitlabSubscribeError)
def subscribe(self, **kwargs):
"""Subscribe to the object notifications.
@@ -442,12 +442,11 @@ class SubscribableMixin(object):
GitlabAuthenticationError: If authentication is not correct
GitlabSubscribeError: If the subscription cannot be done
"""
- path = '%s/%s/subscribe' % (self.manager.path, self.get_id())
+ path = "%s/%s/subscribe" % (self.manager.path, self.get_id())
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action(('ProjectIssue', 'ProjectMergeRequest',
- 'ProjectLabel'))
+ @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest", "ProjectLabel"))
@exc.on_http_error(exc.GitlabUnsubscribeError)
def unsubscribe(self, **kwargs):
"""Unsubscribe from the object notifications.
@@ -459,13 +458,13 @@ class SubscribableMixin(object):
GitlabAuthenticationError: If authentication is not correct
GitlabUnsubscribeError: If the unsubscription cannot be done
"""
- path = '%s/%s/unsubscribe' % (self.manager.path, self.get_id())
+ path = "%s/%s/unsubscribe" % (self.manager.path, self.get_id())
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
class TodoMixin(object):
- @cli.register_custom_action(('ProjectIssue', 'ProjectMergeRequest'))
+ @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
@exc.on_http_error(exc.GitlabTodoError)
def todo(self, **kwargs):
"""Create a todo associated to the object.
@@ -477,12 +476,12 @@ class TodoMixin(object):
GitlabAuthenticationError: If authentication is not correct
GitlabTodoError: If the todo cannot be set
"""
- path = '%s/%s/todo' % (self.manager.path, self.get_id())
+ path = "%s/%s/todo" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path, **kwargs)
class TimeTrackingMixin(object):
- @cli.register_custom_action(('ProjectIssue', 'ProjectMergeRequest'))
+ @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
@exc.on_http_error(exc.GitlabTimeTrackingError)
def time_stats(self, **kwargs):
"""Get time stats for the object.
@@ -496,14 +495,13 @@ class TimeTrackingMixin(object):
"""
# Use the existing time_stats attribute if it exist, otherwise make an
# API call
- if 'time_stats' in self.attributes:
- return self.attributes['time_stats']
+ if "time_stats" in self.attributes:
+ return self.attributes["time_stats"]
- path = '%s/%s/time_stats' % (self.manager.path, self.get_id())
+ path = "%s/%s/time_stats" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)
- @cli.register_custom_action(('ProjectIssue', 'ProjectMergeRequest'),
- ('duration', ))
+ @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"), ("duration",))
@exc.on_http_error(exc.GitlabTimeTrackingError)
def time_estimate(self, duration, **kwargs):
"""Set an estimated time of work for the object.
@@ -516,11 +514,11 @@ class TimeTrackingMixin(object):
GitlabAuthenticationError: If authentication is not correct
GitlabTimeTrackingError: If the time tracking update cannot be done
"""
- path = '%s/%s/time_estimate' % (self.manager.path, self.get_id())
- data = {'duration': duration}
+ path = "%s/%s/time_estimate" % (self.manager.path, self.get_id())
+ data = {"duration": duration}
return self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- @cli.register_custom_action(('ProjectIssue', 'ProjectMergeRequest'))
+ @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
@exc.on_http_error(exc.GitlabTimeTrackingError)
def reset_time_estimate(self, **kwargs):
"""Resets estimated time for the object to 0 seconds.
@@ -532,11 +530,10 @@ class TimeTrackingMixin(object):
GitlabAuthenticationError: If authentication is not correct
GitlabTimeTrackingError: If the time tracking update cannot be done
"""
- path = '%s/%s/reset_time_estimate' % (self.manager.path, self.get_id())
+ path = "%s/%s/reset_time_estimate" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_post(path, **kwargs)
- @cli.register_custom_action(('ProjectIssue', 'ProjectMergeRequest'),
- ('duration', ))
+ @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"), ("duration",))
@exc.on_http_error(exc.GitlabTimeTrackingError)
def add_spent_time(self, duration, **kwargs):
"""Add time spent working on the object.
@@ -549,11 +546,11 @@ class TimeTrackingMixin(object):
GitlabAuthenticationError: If authentication is not correct
GitlabTimeTrackingError: If the time tracking update cannot be done
"""
- path = '%s/%s/add_spent_time' % (self.manager.path, self.get_id())
- data = {'duration': duration}
+ path = "%s/%s/add_spent_time" % (self.manager.path, self.get_id())
+ data = {"duration": duration}
return self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- @cli.register_custom_action(('ProjectIssue', 'ProjectMergeRequest'))
+ @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
@exc.on_http_error(exc.GitlabTimeTrackingError)
def reset_spent_time(self, **kwargs):
"""Resets the time spent working on the object.
@@ -565,12 +562,12 @@ class TimeTrackingMixin(object):
GitlabAuthenticationError: If authentication is not correct
GitlabTimeTrackingError: If the time tracking update cannot be done
"""
- path = '%s/%s/reset_spent_time' % (self.manager.path, self.get_id())
+ path = "%s/%s/reset_spent_time" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_post(path, **kwargs)
class ParticipantsMixin(object):
- @cli.register_custom_action(('ProjectMergeRequest', 'ProjectIssue'))
+ @cli.register_custom_action(("ProjectMergeRequest", "ProjectIssue"))
@exc.on_http_error(exc.GitlabListError)
def participants(self, **kwargs):
"""List the participants.
@@ -591,13 +588,14 @@ class ParticipantsMixin(object):
RESTObjectList: The list of participants
"""
- path = '%s/%s/participants' % (self.manager.path, self.get_id())
+ path = "%s/%s/participants" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)
class BadgeRenderMixin(object):
- @cli.register_custom_action(('GroupBadgeManager', 'ProjectBadgeManager'),
- ('link_url', 'image_url'))
+ @cli.register_custom_action(
+ ("GroupBadgeManager", "ProjectBadgeManager"), ("link_url", "image_url")
+ )
@exc.on_http_error(exc.GitlabRenderError)
def render(self, link_url, image_url, **kwargs):
"""Preview link_url and image_url after interpolation.
@@ -614,6 +612,6 @@ class BadgeRenderMixin(object):
Returns:
dict: The rendering properties
"""
- path = '%s/render' % self.path
- data = {'link_url': link_url, 'image_url': image_url}
+ path = "%s/render" % self.path
+ data = {"link_url": link_url, "image_url": image_url}
return self.gitlab.http_get(path, data, **kwargs)
diff --git a/gitlab/tests/test_base.py b/gitlab/tests/test_base.py
index d38c507..2526bee 100644
--- a/gitlab/tests/test_base.py
+++ b/gitlab/tests/test_base.py
@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pickle
+
try:
import unittest
except ImportError:
@@ -34,23 +35,23 @@ class FakeObject(base.RESTObject):
class FakeManager(base.RESTManager):
_obj_cls = FakeObject
- _path = '/tests'
+ _path = "/tests"
class TestRESTManager(unittest.TestCase):
def test_computed_path_simple(self):
class MGR(base.RESTManager):
- _path = '/tests'
+ _path = "/tests"
_obj_cls = object
mgr = MGR(FakeGitlab())
- self.assertEqual(mgr._computed_path, '/tests')
+ self.assertEqual(mgr._computed_path, "/tests")
def test_computed_path_with_parent(self):
class MGR(base.RESTManager):
- _path = '/tests/%(test_id)s/cases'
+ _path = "/tests/%(test_id)s/cases"
_obj_cls = object
- _from_parent_attrs = {'test_id': 'id'}
+ _from_parent_attrs = {"test_id": "id"}
class Parent(object):
id = 42
@@ -59,15 +60,15 @@ class TestRESTManager(unittest.TestCase):
no_id = 0
mgr = MGR(FakeGitlab(), parent=Parent())
- self.assertEqual(mgr._computed_path, '/tests/42/cases')
+ self.assertEqual(mgr._computed_path, "/tests/42/cases")
def test_path_property(self):
class MGR(base.RESTManager):
- _path = '/tests'
+ _path = "/tests"
_obj_cls = object
mgr = MGR(FakeGitlab())
- self.assertEqual(mgr.path, '/tests')
+ self.assertEqual(mgr.path, "/tests")
class TestRESTObject(unittest.TestCase):
@@ -76,36 +77,36 @@ class TestRESTObject(unittest.TestCase):
self.manager = FakeManager(self.gitlab)
def test_instanciate(self):
- obj = FakeObject(self.manager, {'foo': 'bar'})
+ obj = FakeObject(self.manager, {"foo": "bar"})
- self.assertDictEqual({'foo': 'bar'}, obj._attrs)
+ self.assertDictEqual({"foo": "bar"}, obj._attrs)
self.assertDictEqual({}, obj._updated_attrs)
self.assertEqual(None, obj._create_managers())
self.assertEqual(self.manager, obj.manager)
self.assertEqual(self.gitlab, obj.manager.gitlab)
def test_pickability(self):
- obj = FakeObject(self.manager, {'foo': 'bar'})
+ obj = FakeObject(self.manager, {"foo": "bar"})
original_obj_module = obj._module
pickled = pickle.dumps(obj)
unpickled = pickle.loads(pickled)
self.assertIsInstance(unpickled, FakeObject)
- self.assertTrue(hasattr(unpickled, '_module'))
+ self.assertTrue(hasattr(unpickled, "_module"))
self.assertEqual(unpickled._module, original_obj_module)
def test_attrs(self):
- obj = FakeObject(self.manager, {'foo': 'bar'})
+ obj = FakeObject(self.manager, {"foo": "bar"})
- self.assertEqual('bar', obj.foo)
- self.assertRaises(AttributeError, getattr, obj, 'bar')
+ self.assertEqual("bar", obj.foo)
+ self.assertRaises(AttributeError, getattr, obj, "bar")
- obj.bar = 'baz'
- self.assertEqual('baz', obj.bar)
- self.assertDictEqual({'foo': 'bar'}, obj._attrs)
- self.assertDictEqual({'bar': 'baz'}, obj._updated_attrs)
+ obj.bar = "baz"
+ self.assertEqual("baz", obj.bar)
+ self.assertDictEqual({"foo": "bar"}, obj._attrs)
+ self.assertDictEqual({"bar": "baz"}, obj._updated_attrs)
def test_get_id(self):
- obj = FakeObject(self.manager, {'foo': 'bar'})
+ obj = FakeObject(self.manager, {"foo": "bar"})
obj.id = 42
self.assertEqual(42, obj.get_id())
@@ -114,50 +115,47 @@ class TestRESTObject(unittest.TestCase):
def test_custom_id_attr(self):
class OtherFakeObject(FakeObject):
- _id_attr = 'foo'
+ _id_attr = "foo"
- obj = OtherFakeObject(self.manager, {'foo': 'bar'})
- self.assertEqual('bar', obj.get_id())
+ obj = OtherFakeObject(self.manager, {"foo": "bar"})
+ self.assertEqual("bar", obj.get_id())
def test_update_attrs(self):
- obj = FakeObject(self.manager, {'foo': 'bar'})
- obj.bar = 'baz'
- obj._update_attrs({'foo': 'foo', 'bar': 'bar'})
- self.assertDictEqual({'foo': 'foo', 'bar': 'bar'}, obj._attrs)
+ obj = FakeObject(self.manager, {"foo": "bar"})
+ obj.bar = "baz"
+ obj._update_attrs({"foo": "foo", "bar": "bar"})
+ self.assertDictEqual({"foo": "foo", "bar": "bar"}, obj._attrs)
self.assertDictEqual({}, obj._updated_attrs)
def test_create_managers(self):
class ObjectWithManager(FakeObject):
- _managers = (('fakes', 'FakeManager'), )
+ _managers = (("fakes", "FakeManager"),)
- obj = ObjectWithManager(self.manager, {'foo': 'bar'})
+ obj = ObjectWithManager(self.manager, {"foo": "bar"})
obj.id = 42
self.assertIsInstance(obj.fakes, FakeManager)
self.assertEqual(obj.fakes.gitlab, self.gitlab)
self.assertEqual(obj.fakes._parent, obj)
def test_equality(self):
- obj1 = FakeObject(self.manager, {'id': 'foo'})
- obj2 = FakeObject(self.manager, {'id': 'foo', 'other_attr': 'bar'})
+ obj1 = FakeObject(self.manager, {"id": "foo"})
+ obj2 = FakeObject(self.manager, {"id": "foo", "other_attr": "bar"})
self.assertEqual(obj1, obj2)
def test_equality_custom_id(self):
class OtherFakeObject(FakeObject):
- _id_attr = 'foo'
+ _id_attr = "foo"
- obj1 = OtherFakeObject(self.manager, {'foo': 'bar'})
- obj2 = OtherFakeObject(
- self.manager,
- {'foo': 'bar', 'other_attr': 'baz'}
- )
+ obj1 = OtherFakeObject(self.manager, {"foo": "bar"})
+ obj2 = OtherFakeObject(self.manager, {"foo": "bar", "other_attr": "baz"})
self.assertEqual(obj1, obj2)
def test_inequality(self):
- obj1 = FakeObject(self.manager, {'id': 'foo'})
- obj2 = FakeObject(self.manager, {'id': 'bar'})
+ obj1 = FakeObject(self.manager, {"id": "foo"})
+ obj2 = FakeObject(self.manager, {"id": "bar"})
self.assertNotEqual(obj1, obj2)
def test_inequality_no_id(self):
- obj1 = FakeObject(self.manager, {'attr1': 'foo'})
- obj2 = FakeObject(self.manager, {'attr1': 'bar'})
+ obj1 = FakeObject(self.manager, {"attr1": "foo"})
+ obj2 = FakeObject(self.manager, {"attr1": "bar"})
self.assertNotEqual(obj1, obj2)
diff --git a/gitlab/tests/test_cli.py b/gitlab/tests/test_cli.py
index 3fe4a4e..bc49d8b 100644
--- a/gitlab/tests/test_cli.py
+++ b/gitlab/tests/test_cli.py
@@ -22,6 +22,7 @@ from __future__ import absolute_import
import argparse
import os
import tempfile
+
try:
from contextlib import redirect_stderr # noqa: H302
except ImportError:
@@ -34,6 +35,7 @@ except ImportError:
yield
sys.stderr = old_target
+
try:
import unittest
except ImportError:
@@ -69,8 +71,8 @@ class TestCLI(unittest.TestCase):
self.assertEqual(test.exception.code, 1)
def test_parse_value(self):
- ret = cli._parse_value('foobar')
- self.assertEqual(ret, 'foobar')
+ ret = cli._parse_value("foobar")
+ self.assertEqual(ret, "foobar")
ret = cli._parse_value(True)
self.assertEqual(ret, True)
@@ -82,36 +84,39 @@ class TestCLI(unittest.TestCase):
self.assertEqual(ret, None)
fd, temp_path = tempfile.mkstemp()
- os.write(fd, b'content')
+ os.write(fd, b"content")
os.close(fd)
- ret = cli._parse_value('@%s' % temp_path)
- self.assertEqual(ret, 'content')
+ ret = cli._parse_value("@%s" % temp_path)
+ self.assertEqual(ret, "content")
os.unlink(temp_path)
fl = six.StringIO()
with redirect_stderr(fl):
with self.assertRaises(SystemExit) as exc:
- cli._parse_value('@/thisfileprobablydoesntexist')
- self.assertEqual(fl.getvalue(),
- "[Errno 2] No such file or directory:"
- " '/thisfileprobablydoesntexist'\n")
+ cli._parse_value("@/thisfileprobablydoesntexist")
+ self.assertEqual(
+ fl.getvalue(),
+ "[Errno 2] No such file or directory:"
+ " '/thisfileprobablydoesntexist'\n",
+ )
self.assertEqual(exc.exception.code, 1)
def test_base_parser(self):
parser = cli._get_base_parser()
- args = parser.parse_args(['-v', '-g', 'gl_id',
- '-c', 'foo.cfg', '-c', 'bar.cfg'])
+ args = parser.parse_args(
+ ["-v", "-g", "gl_id", "-c", "foo.cfg", "-c", "bar.cfg"]
+ )
self.assertTrue(args.verbose)
- self.assertEqual(args.gitlab, 'gl_id')
- self.assertEqual(args.config_file, ['foo.cfg', 'bar.cfg'])
+ self.assertEqual(args.gitlab, "gl_id")
+ self.assertEqual(args.config_file, ["foo.cfg", "bar.cfg"])
class TestV4CLI(unittest.TestCase):
def test_parse_args(self):
parser = cli._get_parser(gitlab.v4.cli)
- args = parser.parse_args(['project', 'list'])
- self.assertEqual(args.what, 'project')
- self.assertEqual(args.action, 'list')
+ args = parser.parse_args(["project", "list"])
+ self.assertEqual(args.what, "project")
+ self.assertEqual(args.action, "list")
def test_parser(self):
parser = cli._get_parser(gitlab.v4.cli)
@@ -121,29 +126,29 @@ class TestV4CLI(unittest.TestCase):
subparsers = action
break
self.assertIsNotNone(subparsers)
- self.assertIn('project', subparsers.choices)
+ self.assertIn("project", subparsers.choices)
user_subparsers = None
- for action in subparsers.choices['project']._actions:
+ for action in subparsers.choices["project"]._actions:
if type(action) == argparse._SubParsersAction:
user_subparsers = action
break
self.assertIsNotNone(user_subparsers)
- self.assertIn('list', user_subparsers.choices)
- self.assertIn('get', user_subparsers.choices)
- self.assertIn('delete', user_subparsers.choices)
- self.assertIn('update', user_subparsers.choices)
- self.assertIn('create', user_subparsers.choices)
- self.assertIn('archive', user_subparsers.choices)
- self.assertIn('unarchive', user_subparsers.choices)
+ self.assertIn("list", user_subparsers.choices)
+ self.assertIn("get", user_subparsers.choices)
+ self.assertIn("delete", user_subparsers.choices)
+ self.assertIn("update", user_subparsers.choices)
+ self.assertIn("create", user_subparsers.choices)
+ self.assertIn("archive", user_subparsers.choices)
+ self.assertIn("unarchive", user_subparsers.choices)
- actions = user_subparsers.choices['create']._option_string_actions
- self.assertFalse(actions['--description'].required)
+ actions = user_subparsers.choices["create"]._option_string_actions
+ self.assertFalse(actions["--description"].required)
user_subparsers = None
- for action in subparsers.choices['group']._actions:
+ for action in subparsers.choices["group"]._actions:
if type(action) == argparse._SubParsersAction:
user_subparsers = action
break
- actions = user_subparsers.choices['create']._option_string_actions
- self.assertTrue(actions['--name'].required)
+ actions = user_subparsers.choices["create"]._option_string_actions
+ self.assertTrue(actions["--name"].required)
diff --git a/gitlab/tests/test_config.py b/gitlab/tests/test_config.py
index d1e668e..9e19ce8 100644
--- a/gitlab/tests/test_config.py
+++ b/gitlab/tests/test_config.py
@@ -76,51 +76,51 @@ per_page = 200
class TestConfigParser(unittest.TestCase):
- @mock.patch('os.path.exists')
+ @mock.patch("os.path.exists")
def test_missing_config(self, path_exists):
path_exists.return_value = False
with self.assertRaises(config.GitlabConfigMissingError):
- config.GitlabConfigParser('test')
+ config.GitlabConfigParser("test")
- @mock.patch('os.path.exists')
- @mock.patch('six.moves.builtins.open')
+ @mock.patch("os.path.exists")
+ @mock.patch("six.moves.builtins.open")
def test_invalid_id(self, m_open, path_exists):
fd = six.StringIO(no_default_config)
fd.close = mock.Mock(return_value=None)
m_open.return_value = fd
path_exists.return_value = True
- config.GitlabConfigParser('there')
+ config.GitlabConfigParser("there")
self.assertRaises(config.GitlabIDError, config.GitlabConfigParser)
fd = six.StringIO(valid_config)
fd.close = mock.Mock(return_value=None)
m_open.return_value = fd
- self.assertRaises(config.GitlabDataError,
- config.GitlabConfigParser,
- gitlab_id='not_there')
+ self.assertRaises(
+ config.GitlabDataError, config.GitlabConfigParser, gitlab_id="not_there"
+ )
- @mock.patch('os.path.exists')
- @mock.patch('six.moves.builtins.open')
+ @mock.patch("os.path.exists")
+ @mock.patch("six.moves.builtins.open")
def test_invalid_data(self, m_open, path_exists):
fd = six.StringIO(missing_attr_config)
- fd.close = mock.Mock(return_value=None,
- side_effect=lambda: fd.seek(0))
+ fd.close = mock.Mock(return_value=None, side_effect=lambda: fd.seek(0))
m_open.return_value = fd
path_exists.return_value = True
- config.GitlabConfigParser('one')
- config.GitlabConfigParser('one')
- self.assertRaises(config.GitlabDataError, config.GitlabConfigParser,
- gitlab_id='two')
- self.assertRaises(config.GitlabDataError, config.GitlabConfigParser,
- gitlab_id='three')
+ config.GitlabConfigParser("one")
+ config.GitlabConfigParser("one")
+ self.assertRaises(
+ config.GitlabDataError, config.GitlabConfigParser, gitlab_id="two"
+ )
+ self.assertRaises(
+ config.GitlabDataError, config.GitlabConfigParser, gitlab_id="three"
+ )
with self.assertRaises(config.GitlabDataError) as emgr:
- config.GitlabConfigParser('four')
- self.assertEqual('Unsupported per_page number: 200',
- emgr.exception.args[0])
+ config.GitlabConfigParser("four")
+ self.assertEqual("Unsupported per_page number: 200", emgr.exception.args[0])
- @mock.patch('os.path.exists')
- @mock.patch('six.moves.builtins.open')
+ @mock.patch("os.path.exists")
+ @mock.patch("six.moves.builtins.open")
def test_valid_data(self, m_open, path_exists):
fd = six.StringIO(valid_config)
fd.close = mock.Mock(return_value=None)
diff --git a/gitlab/tests/test_gitlab.py b/gitlab/tests/test_gitlab.py
index fddd5ed..c2b372a 100644
--- a/gitlab/tests/test_gitlab.py
+++ b/gitlab/tests/test_gitlab.py
@@ -21,6 +21,7 @@ from __future__ import print_function
import os
import pickle
import tempfile
+
try:
import unittest
except ImportError:
@@ -64,42 +65,52 @@ class TestSanitize(unittest.TestCase):
class TestGitlabList(unittest.TestCase):
def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- api_version=4)
+ self.gl = Gitlab(
+ "http://localhost", private_token="private_token", api_version=4
+ )
def test_build_list(self):
- @urlmatch(scheme='http', netloc="localhost", path="/api/v4/tests",
- method="get")
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get")
def resp_1(url, request):
- headers = {'content-type': 'application/json',
- 'X-Page': 1,
- 'X-Next-Page': 2,
- 'X-Per-Page': 1,
- 'X-Total-Pages': 2,
- 'X-Total': 2,
- 'Link': (
- '<http://localhost/api/v4/tests?per_page=1&page=2>;'
- ' rel="next"')}
+ headers = {
+ "content-type": "application/json",
+ "X-Page": 1,
+ "X-Next-Page": 2,
+ "X-Per-Page": 1,
+ "X-Total-Pages": 2,
+ "X-Total": 2,
+ "Link": (
+ "<http://localhost/api/v4/tests?per_page=1&page=2>;" ' rel="next"'
+ ),
+ }
content = '[{"a": "b"}]'
return response(200, content, headers, None, 5, request)
- @urlmatch(scheme='http', netloc="localhost", path="/api/v4/tests",
- method='get', query=r'.*page=2')
+ @urlmatch(
+ scheme="http",
+ netloc="localhost",
+ path="/api/v4/tests",
+ method="get",
+ query=r".*page=2",
+ )
def resp_2(url, request):
- headers = {'content-type': 'application/json',
- 'X-Page': 2,
- 'X-Next-Page': 2,
- 'X-Per-Page': 1,
- 'X-Total-Pages': 2,
- 'X-Total': 2}
+ headers = {
+ "content-type": "application/json",
+ "X-Page": 2,
+ "X-Next-Page": 2,
+ "X-Per-Page": 1,
+ "X-Total-Pages": 2,
+ "X-Total": 2,
+ }
content = '[{"c": "d"}]'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_1):
- obj = self.gl.http_list('/tests', as_list=False)
+ obj = self.gl.http_list("/tests", as_list=False)
self.assertEqual(len(obj), 2)
- self.assertEqual(obj._next_url,
- 'http://localhost/api/v4/tests?per_page=1&page=2')
+ self.assertEqual(
+ obj._next_url, "http://localhost/api/v4/tests?per_page=1&page=2"
+ )
self.assertEqual(obj.current_page, 1)
self.assertEqual(obj.prev_page, None)
self.assertEqual(obj.next_page, 2)
@@ -110,306 +121,343 @@ class TestGitlabList(unittest.TestCase):
with HTTMock(resp_2):
l = list(obj)
self.assertEqual(len(l), 2)
- self.assertEqual(l[0]['a'], 'b')
- self.assertEqual(l[1]['c'], 'd')
+ self.assertEqual(l[0]["a"], "b")
+ self.assertEqual(l[1]["c"], "d")
class TestGitlabHttpMethods(unittest.TestCase):
def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- api_version=4)
+ self.gl = Gitlab(
+ "http://localhost", private_token="private_token", api_version=4
+ )
def test_build_url(self):
- r = self.gl._build_url('http://localhost/api/v4')
- self.assertEqual(r, 'http://localhost/api/v4')
- r = self.gl._build_url('https://localhost/api/v4')
- self.assertEqual(r, 'https://localhost/api/v4')
- r = self.gl._build_url('/projects')
- self.assertEqual(r, 'http://localhost/api/v4/projects')
+ r = self.gl._build_url("http://localhost/api/v4")
+ self.assertEqual(r, "http://localhost/api/v4")
+ r = self.gl._build_url("https://localhost/api/v4")
+ self.assertEqual(r, "https://localhost/api/v4")
+ r = self.gl._build_url("/projects")
+ self.assertEqual(r, "http://localhost/api/v4/projects")
def test_http_request(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="get"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '[{"name": "project1"}]'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- http_r = self.gl.http_request('get', '/projects')
+ http_r = self.gl.http_request("get", "/projects")
http_r.json()
self.assertEqual(http_r.status_code, 200)
def test_http_request_404(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/not_there", method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/not_there", method="get"
+ )
def resp_cont(url, request):
- content = {'Here is wh it failed'}
+ content = {"Here is wh it failed"}
return response(404, content, {}, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabHttpError,
- self.gl.http_request,
- 'get', '/not_there')
+ self.assertRaises(
+ GitlabHttpError, self.gl.http_request, "get", "/not_there"
+ )
def test_get_request(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="get"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{"name": "project1"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- result = self.gl.http_get('/projects')
+ result = self.gl.http_get("/projects")
self.assertIsInstance(result, dict)
- self.assertEqual(result['name'], 'project1')
+ self.assertEqual(result["name"], "project1")
def test_get_request_raw(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="get"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/octet-stream'}
- content = 'content'
+ headers = {"content-type": "application/octet-stream"}
+ content = "content"
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- result = self.gl.http_get('/projects')
- self.assertEqual(result.content.decode('utf-8'), 'content')
+ result = self.gl.http_get("/projects")
+ self.assertEqual(result.content.decode("utf-8"), "content")
def test_get_request_404(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/not_there", method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/not_there", method="get"
+ )
def resp_cont(url, request):
- content = {'Here is wh it failed'}
+ content = {"Here is wh it failed"}
return response(404, content, {}, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabHttpError, self.gl.http_get, '/not_there')
+ self.assertRaises(GitlabHttpError, self.gl.http_get, "/not_there")
def test_get_request_invalid_data(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="get"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '["name": "project1"]'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabParsingError, self.gl.http_get,
- '/projects')
+ self.assertRaises(GitlabParsingError, self.gl.http_get, "/projects")
def test_list_request(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="get"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json', 'X-Total': 1}
+ headers = {"content-type": "application/json", "X-Total": 1}
content = '[{"name": "project1"}]'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- result = self.gl.http_list('/projects', as_list=True)
+ result = self.gl.http_list("/projects", as_list=True)
self.assertIsInstance(result, list)
self.assertEqual(len(result), 1)
with HTTMock(resp_cont):
- result = self.gl.http_list('/projects', as_list=False)
+ result = self.gl.http_list("/projects", as_list=False)
self.assertIsInstance(result, GitlabList)
self.assertEqual(len(result), 1)
with HTTMock(resp_cont):
- result = self.gl.http_list('/projects', all=True)
+ result = self.gl.http_list("/projects", all=True)
self.assertIsInstance(result, list)
self.assertEqual(len(result), 1)
def test_list_request_404(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/not_there", method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/not_there", method="get"
+ )
def resp_cont(url, request):
- content = {'Here is why it failed'}
+ content = {"Here is why it failed"}
return response(404, content, {}, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabHttpError, self.gl.http_list, '/not_there')
+ self.assertRaises(GitlabHttpError, self.gl.http_list, "/not_there")
def test_list_request_invalid_data(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="get"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '["name": "project1"]'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabParsingError, self.gl.http_list,
- '/projects')
+ self.assertRaises(GitlabParsingError, self.gl.http_list, "/projects")
def test_post_request(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="post")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="post"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{"name": "project1"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- result = self.gl.http_post('/projects')
+ result = self.gl.http_post("/projects")
self.assertIsInstance(result, dict)
- self.assertEqual(result['name'], 'project1')
+ self.assertEqual(result["name"], "project1")
def test_post_request_404(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/not_there", method="post")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/not_there", method="post"
+ )
def resp_cont(url, request):
- content = {'Here is wh it failed'}
+ content = {"Here is wh it failed"}
return response(404, content, {}, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabHttpError, self.gl.http_post, '/not_there')
+ self.assertRaises(GitlabHttpError, self.gl.http_post, "/not_there")
def test_post_request_invalid_data(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="post")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="post"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '["name": "project1"]'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabParsingError, self.gl.http_post,
- '/projects')
+ self.assertRaises(GitlabParsingError, self.gl.http_post, "/projects")
def test_put_request(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="put")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="put"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{"name": "project1"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- result = self.gl.http_put('/projects')
+ result = self.gl.http_put("/projects")
self.assertIsInstance(result, dict)
- self.assertEqual(result['name'], 'project1')
+ self.assertEqual(result["name"], "project1")
def test_put_request_404(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/not_there", method="put")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/not_there", method="put"
+ )
def resp_cont(url, request):
- content = {'Here is wh it failed'}
+ content = {"Here is wh it failed"}
return response(404, content, {}, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabHttpError, self.gl.http_put, '/not_there')
+ self.assertRaises(GitlabHttpError, self.gl.http_put, "/not_there")
def test_put_request_invalid_data(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="put")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="put"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '["name": "project1"]'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabParsingError, self.gl.http_put,
- '/projects')
+ self.assertRaises(GitlabParsingError, self.gl.http_put, "/projects")
def test_delete_request(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="delete")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects", method="delete"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = 'true'
+ headers = {"content-type": "application/json"}
+ content = "true"
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
- result = self.gl.http_delete('/projects')
+ result = self.gl.http_delete("/projects")
self.assertIsInstance(result, requests.Response)
self.assertEqual(result.json(), True)
def test_delete_request_404(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/not_there", method="delete")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/not_there", method="delete"
+ )
def resp_cont(url, request):
- content = {'Here is wh it failed'}
+ content = {"Here is wh it failed"}
return response(404, content, {}, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabHttpError, self.gl.http_delete,
- '/not_there')
+ self.assertRaises(GitlabHttpError, self.gl.http_delete, "/not_there")
class TestGitlabAuth(unittest.TestCase):
def test_invalid_auth_args(self):
- self.assertRaises(ValueError,
- Gitlab,
- "http://localhost", api_version='4',
- private_token='private_token', oauth_token='bearer')
- self.assertRaises(ValueError,
- Gitlab,
- "http://localhost", api_version='4',
- oauth_token='bearer', http_username='foo',
- http_password='bar')
- self.assertRaises(ValueError,
- Gitlab,
- "http://localhost", api_version='4',
- private_token='private_token', http_password='bar')
- self.assertRaises(ValueError,
- Gitlab,
- "http://localhost", api_version='4',
- private_token='private_token', http_username='foo')
+ self.assertRaises(
+ ValueError,
+ Gitlab,
+ "http://localhost",
+ api_version="4",
+ private_token="private_token",
+ oauth_token="bearer",
+ )
+ self.assertRaises(
+ ValueError,
+ Gitlab,
+ "http://localhost",
+ api_version="4",
+ oauth_token="bearer",
+ http_username="foo",
+ http_password="bar",
+ )
+ self.assertRaises(
+ ValueError,
+ Gitlab,
+ "http://localhost",
+ api_version="4",
+ private_token="private_token",
+ http_password="bar",
+ )
+ self.assertRaises(
+ ValueError,
+ Gitlab,
+ "http://localhost",
+ api_version="4",
+ private_token="private_token",
+ http_username="foo",
+ )
def test_private_token_auth(self):
- gl = Gitlab('http://localhost', private_token='private_token',
- api_version='4')
- self.assertEqual(gl.private_token, 'private_token')
+ gl = Gitlab("http://localhost", private_token="private_token", api_version="4")
+ self.assertEqual(gl.private_token, "private_token")
self.assertEqual(gl.oauth_token, None)
self.assertEqual(gl._http_auth, None)
- self.assertEqual(gl.headers['PRIVATE-TOKEN'], 'private_token')
- self.assertNotIn('Authorization', gl.headers)
+ self.assertEqual(gl.headers["PRIVATE-TOKEN"], "private_token")
+ self.assertNotIn("Authorization", gl.headers)
def test_oauth_token_auth(self):
- gl = Gitlab('http://localhost', oauth_token='oauth_token',
- api_version='4')
+ gl = Gitlab("http://localhost", oauth_token="oauth_token", api_version="4")
self.assertEqual(gl.private_token, None)
- self.assertEqual(gl.oauth_token, 'oauth_token')
+ self.assertEqual(gl.oauth_token, "oauth_token")
self.assertEqual(gl._http_auth, None)
- self.assertEqual(gl.headers['Authorization'], 'Bearer oauth_token')
- self.assertNotIn('PRIVATE-TOKEN', gl.headers)
+ self.assertEqual(gl.headers["Authorization"], "Bearer oauth_token")
+ self.assertNotIn("PRIVATE-TOKEN", gl.headers)
def test_http_auth(self):
- gl = Gitlab('http://localhost', private_token='private_token',
- http_username='foo', http_password='bar', api_version='4')
- self.assertEqual(gl.private_token, 'private_token')
+ gl = Gitlab(
+ "http://localhost",
+ private_token="private_token",
+ http_username="foo",
+ http_password="bar",
+ api_version="4",
+ )
+ self.assertEqual(gl.private_token, "private_token")
self.assertEqual(gl.oauth_token, None)
self.assertIsInstance(gl._http_auth, requests.auth.HTTPBasicAuth)
- self.assertEqual(gl.headers['PRIVATE-TOKEN'], 'private_token')
- self.assertNotIn('Authorization', gl.headers)
+ self.assertEqual(gl.headers["PRIVATE-TOKEN"], "private_token")
+ self.assertNotIn("Authorization", gl.headers)
class TestGitlab(unittest.TestCase):
-
def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True, api_version=4)
+ self.gl = Gitlab(
+ "http://localhost",
+ private_token="private_token",
+ email="testuser@test.com",
+ password="testpassword",
+ ssl_verify=True,
+ api_version=4,
+ )
def test_pickability(self):
original_gl_objects = self.gl._objects
pickled = pickle.dumps(self.gl)
unpickled = pickle.loads(pickled)
self.assertIsInstance(unpickled, Gitlab)
- self.assertTrue(hasattr(unpickled, '_objects'))
+ self.assertTrue(hasattr(unpickled, "_objects"))
self.assertEqual(unpickled._objects, original_gl_objects)
def test_credentials_auth_nopassword(self):
self.gl.email = None
self.gl.password = None
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/session",
- method="post")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/session", method="post"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{"message": "message"}'.encode("utf-8")
return response(404, content, headers, None, 5, request)
@@ -417,10 +465,11 @@ class TestGitlab(unittest.TestCase):
self.assertRaises(GitlabHttpError, self.gl._credentials_auth)
def test_credentials_auth_notok(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/session",
- method="post")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/session", method="post"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{"message": "message"}'.encode("utf-8")
return response(404, content, headers, None, 5, request)
@@ -441,12 +490,14 @@ class TestGitlab(unittest.TestCase):
id_ = 1
expected = {"PRIVATE-TOKEN": token}
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/session",
- method="post")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/session", method="post"
+ )
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{{"id": {0:d}, "private_token": "{1:s}"}}'.format(
- id_, token).encode("utf-8")
+ id_, token
+ ).encode("utf-8")
return response(201, content, headers, None, 5, request)
with HTTMock(resp_cont):
@@ -461,12 +512,12 @@ class TestGitlab(unittest.TestCase):
name = "username"
id_ = 1
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/user",
- method="get")
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/user", method="get")
def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{{"id": {0:d}, "username": "{1:s}"}}'.format(
- id_, name).encode("utf-8")
+ headers = {"content-type": "application/json"}
+ content = '{{"id": {0:d}, "username": "{1:s}"}}'.format(id_, name).encode(
+ "utf-8"
+ )
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
@@ -476,10 +527,11 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(type(self.gl.user), CurrentUser)
def test_hooks(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/hooks/1",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/hooks/1", method="get"
+ )
def resp_get_hook(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{"url": "testurl", "id": 1}'.encode("utf-8")
return response(200, content, headers, None, 5, request)
@@ -490,10 +542,11 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(data.id, 1)
def test_projects(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/projects/1", method="get"
+ )
def resp_get_project(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{"name": "name", "id": 1}'.encode("utf-8")
return response(200, content, headers, None, 5, request)
@@ -504,12 +557,13 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(data.id, 1)
def test_groups(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/groups/1", method="get"
+ )
def resp_get_group(url, request):
- headers = {'content-type': 'application/json'}
+ headers = {"content-type": "application/json"}
content = '{"name": "name", "id": 1, "path": "path"}'
- content = content.encode('utf-8')
+ content = content.encode("utf-8")
return response(200, content, headers, None, 5, request)
with HTTMock(resp_get_group):
@@ -520,27 +574,30 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(data.id, 1)
def test_issues(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/issues",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/issues", method="get"
+ )
def resp_get_issue(url, request):
- headers = {'content-type': 'application/json'}
- content = ('[{"name": "name", "id": 1}, '
- '{"name": "other_name", "id": 2}]')
+ headers = {"content-type": "application/json"}
+ content = '[{"name": "name", "id": 1}, ' '{"name": "other_name", "id": 2}]'
content = content.encode("utf-8")
return response(200, content, headers, None, 5, request)
with HTTMock(resp_get_issue):
data = self.gl.issues.list()
self.assertEqual(data[1].id, 2)
- self.assertEqual(data[1].name, 'other_name')
+ self.assertEqual(data[1].name, "other_name")
def test_users(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1",
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/users/1", method="get"
+ )
def resp_get_user(url, request):
- headers = {'content-type': 'application/json'}
- content = ('{"name": "name", "id": 1, "password": "password", '
- '"username": "username", "email": "email"}')
+ headers = {"content-type": "application/json"}
+ content = (
+ '{"name": "name", "id": 1, "password": "password", '
+ '"username": "username", "email": "email"}'
+ )
content = content.encode("utf-8")
return response(200, content, headers, None, 5, request)
@@ -558,13 +615,14 @@ class TestGitlab(unittest.TestCase):
def test_from_config(self):
config_path = self._default_config()
- gitlab.Gitlab.from_config('one', [config_path])
+ gitlab.Gitlab.from_config("one", [config_path])
os.unlink(config_path)
def test_subclass_from_config(self):
class MyGitlab(gitlab.Gitlab):
pass
+
config_path = self._default_config()
- gl = MyGitlab.from_config('one', [config_path])
- self.assertEqual(type(gl).__name__, 'MyGitlab')
+ gl = MyGitlab.from_config("one", [config_path])
+ self.assertEqual(type(gl).__name__, "MyGitlab")
os.unlink(config_path)
diff --git a/gitlab/tests/test_mixins.py b/gitlab/tests/test_mixins.py
index b3c2e81..56be8f3 100644
--- a/gitlab/tests/test_mixins.py
+++ b/gitlab/tests/test_mixins.py
@@ -38,47 +38,47 @@ class TestObjectMixinsAttributes(unittest.TestCase):
pass
obj = O()
- self.assertTrue(hasattr(obj, 'approve'))
+ self.assertTrue(hasattr(obj, "approve"))
def test_subscribable_mixin(self):
class O(SubscribableMixin):
pass
obj = O()
- self.assertTrue(hasattr(obj, 'subscribe'))
- self.assertTrue(hasattr(obj, 'unsubscribe'))
+ self.assertTrue(hasattr(obj, "subscribe"))
+ self.assertTrue(hasattr(obj, "unsubscribe"))
def test_todo_mixin(self):
class O(TodoMixin):
pass
obj = O()
- self.assertTrue(hasattr(obj, 'todo'))
+ self.assertTrue(hasattr(obj, "todo"))
def test_time_tracking_mixin(self):
class O(TimeTrackingMixin):
pass
obj = O()
- self.assertTrue(hasattr(obj, 'time_stats'))
- self.assertTrue(hasattr(obj, 'time_estimate'))
- self.assertTrue(hasattr(obj, 'reset_time_estimate'))
- self.assertTrue(hasattr(obj, 'add_spent_time'))
- self.assertTrue(hasattr(obj, 'reset_spent_time'))
+ self.assertTrue(hasattr(obj, "time_stats"))
+ self.assertTrue(hasattr(obj, "time_estimate"))
+ self.assertTrue(hasattr(obj, "reset_time_estimate"))
+ self.assertTrue(hasattr(obj, "add_spent_time"))
+ self.assertTrue(hasattr(obj, "reset_spent_time"))
def test_set_mixin(self):
class O(SetMixin):
pass
obj = O()
- self.assertTrue(hasattr(obj, 'set'))
+ self.assertTrue(hasattr(obj, "set"))
def test_user_agent_detail_mixin(self):
class O(UserAgentDetailMixin):
pass
obj = O()
- self.assertTrue(hasattr(obj, 'user_agent_detail'))
+ self.assertTrue(hasattr(obj, "user_agent_detail"))
class TestMetaMixins(unittest.TestCase):
@@ -87,11 +87,11 @@ class TestMetaMixins(unittest.TestCase):
pass
obj = M()
- self.assertTrue(hasattr(obj, 'list'))
- self.assertTrue(hasattr(obj, 'get'))
- self.assertFalse(hasattr(obj, 'create'))
- self.assertFalse(hasattr(obj, 'update'))
- self.assertFalse(hasattr(obj, 'delete'))
+ self.assertTrue(hasattr(obj, "list"))
+ self.assertTrue(hasattr(obj, "get"))
+ self.assertFalse(hasattr(obj, "create"))
+ self.assertFalse(hasattr(obj, "update"))
+ self.assertFalse(hasattr(obj, "delete"))
self.assertIsInstance(obj, ListMixin)
self.assertIsInstance(obj, GetMixin)
@@ -100,11 +100,11 @@ class TestMetaMixins(unittest.TestCase):
pass
obj = M()
- self.assertTrue(hasattr(obj, 'get'))
- self.assertTrue(hasattr(obj, 'list'))
- self.assertTrue(hasattr(obj, 'create'))
- self.assertTrue(hasattr(obj, 'update'))
- self.assertTrue(hasattr(obj, 'delete'))
+ self.assertTrue(hasattr(obj, "get"))
+ self.assertTrue(hasattr(obj, "list"))
+ self.assertTrue(hasattr(obj, "create"))
+ self.assertTrue(hasattr(obj, "update"))
+ self.assertTrue(hasattr(obj, "delete"))
self.assertIsInstance(obj, ListMixin)
self.assertIsInstance(obj, GetMixin)
self.assertIsInstance(obj, CreateMixin)
@@ -116,11 +116,11 @@ class TestMetaMixins(unittest.TestCase):
pass
obj = M()
- self.assertTrue(hasattr(obj, 'get'))
- self.assertTrue(hasattr(obj, 'list'))
- self.assertTrue(hasattr(obj, 'create'))
- self.assertFalse(hasattr(obj, 'update'))
- self.assertTrue(hasattr(obj, 'delete'))
+ self.assertTrue(hasattr(obj, "get"))
+ self.assertTrue(hasattr(obj, "list"))
+ self.assertTrue(hasattr(obj, "create"))
+ self.assertFalse(hasattr(obj, "update"))
+ self.assertTrue(hasattr(obj, "delete"))
self.assertIsInstance(obj, ListMixin)
self.assertIsInstance(obj, GetMixin)
self.assertIsInstance(obj, CreateMixin)
@@ -133,23 +133,25 @@ class FakeObject(base.RESTObject):
class FakeManager(base.RESTManager):
- _path = '/tests'
+ _path = "/tests"
_obj_cls = FakeObject
class TestMixinMethods(unittest.TestCase):
def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- api_version=4)
+ self.gl = Gitlab(
+ "http://localhost", private_token="private_token", api_version=4
+ )
def test_get_mixin(self):
class M(GetMixin, FakeManager):
pass
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/42',
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/tests/42", method="get"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"id": 42, "foo": "bar"}'
return response(200, content, headers, None, 5, request)
@@ -157,36 +159,36 @@ class TestMixinMethods(unittest.TestCase):
mgr = M(self.gl)
obj = mgr.get(42)
self.assertIsInstance(obj, FakeObject)
- self.assertEqual(obj.foo, 'bar')
+ self.assertEqual(obj.foo, "bar")
self.assertEqual(obj.id, 42)
def test_refresh_mixin(self):
class O(RefreshMixin, FakeObject):
pass
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/42',
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/tests/42", method="get"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"id": 42, "foo": "bar"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
mgr = FakeManager(self.gl)
- obj = O(mgr, {'id': 42})
+ obj = O(mgr, {"id": 42})
res = obj.refresh()
self.assertIsNone(res)
- self.assertEqual(obj.foo, 'bar')
+ self.assertEqual(obj.foo, "bar")
self.assertEqual(obj.id, 42)
def test_get_without_id_mixin(self):
class M(GetWithoutIdMixin, FakeManager):
pass
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
- method="get")
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get")
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"foo": "bar"}'
return response(200, content, headers, None, 5, request)
@@ -194,17 +196,16 @@ class TestMixinMethods(unittest.TestCase):
mgr = M(self.gl)
obj = mgr.get()
self.assertIsInstance(obj, FakeObject)
- self.assertEqual(obj.foo, 'bar')
- self.assertFalse(hasattr(obj, 'id'))
+ self.assertEqual(obj.foo, "bar")
+ self.assertFalse(hasattr(obj, "id"))
def test_list_mixin(self):
class M(ListMixin, FakeManager):
pass
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
- method="get")
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get")
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '[{"id": 42, "foo": "bar"},{"id": 43, "foo": "baz"}]'
return response(200, content, headers, None, 5, request)
@@ -229,20 +230,21 @@ class TestMixinMethods(unittest.TestCase):
class M(ListMixin, FakeManager):
pass
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/others',
- method="get")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/others", method="get"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '[{"id": 42, "foo": "bar"}]'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
mgr = M(self.gl)
- obj_list = mgr.list(path='/others', as_list=False)
+ obj_list = mgr.list(path="/others", as_list=False)
self.assertIsInstance(obj_list, base.RESTObjectList)
obj = obj_list.next()
self.assertEqual(obj.id, 42)
- self.assertEqual(obj.foo, 'bar')
+ self.assertEqual(obj.foo, "bar")
self.assertRaises(StopIteration, obj_list.next)
def test_create_mixin_get_attrs(self):
@@ -250,8 +252,8 @@ class TestMixinMethods(unittest.TestCase):
pass
class M2(CreateMixin, FakeManager):
- _create_attrs = (('foo',), ('bar', 'baz'))
- _update_attrs = (('foo',), ('bam', ))
+ _create_attrs = (("foo",), ("bar", "baz"))
+ _update_attrs = (("foo",), ("bam",))
mgr = M1(self.gl)
required, optional = mgr.get_create_attrs()
@@ -260,69 +262,71 @@ class TestMixinMethods(unittest.TestCase):
mgr = M2(self.gl)
required, optional = mgr.get_create_attrs()
- self.assertIn('foo', required)
- self.assertIn('bar', optional)
- self.assertIn('baz', optional)
- self.assertNotIn('bam', optional)
+ self.assertIn("foo", required)
+ self.assertIn("bar", optional)
+ self.assertIn("baz", optional)
+ self.assertNotIn("bam", optional)
def test_create_mixin_missing_attrs(self):
class M(CreateMixin, FakeManager):
- _create_attrs = (('foo',), ('bar', 'baz'))
+ _create_attrs = (("foo",), ("bar", "baz"))
mgr = M(self.gl)
- data = {'foo': 'bar', 'baz': 'blah'}
+ data = {"foo": "bar", "baz": "blah"}
mgr._check_missing_create_attrs(data)
- data = {'baz': 'blah'}
+ data = {"baz": "blah"}
with self.assertRaises(AttributeError) as error:
mgr._check_missing_create_attrs(data)
- self.assertIn('foo', str(error.exception))
+ self.assertIn("foo", str(error.exception))
def test_create_mixin(self):
class M(CreateMixin, FakeManager):
- _create_attrs = (('foo',), ('bar', 'baz'))
- _update_attrs = (('foo',), ('bam', ))
+ _create_attrs = (("foo",), ("bar", "baz"))
+ _update_attrs = (("foo",), ("bam",))
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
- method="post")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/tests", method="post"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"id": 42, "foo": "bar"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
mgr = M(self.gl)
- obj = mgr.create({'foo': 'bar'})
+ obj = mgr.create({"foo": "bar"})
self.assertIsInstance(obj, FakeObject)
self.assertEqual(obj.id, 42)
- self.assertEqual(obj.foo, 'bar')
+ self.assertEqual(obj.foo, "bar")
def test_create_mixin_custom_path(self):
class M(CreateMixin, FakeManager):
- _create_attrs = (('foo',), ('bar', 'baz'))
- _update_attrs = (('foo',), ('bam', ))
+ _create_attrs = (("foo",), ("bar", "baz"))
+ _update_attrs = (("foo",), ("bam",))
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/others',
- method="post")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/others", method="post"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"id": 42, "foo": "bar"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
mgr = M(self.gl)
- obj = mgr.create({'foo': 'bar'}, path='/others')
+ obj = mgr.create({"foo": "bar"}, path="/others")
self.assertIsInstance(obj, FakeObject)
self.assertEqual(obj.id, 42)
- self.assertEqual(obj.foo, 'bar')
+ self.assertEqual(obj.foo, "bar")
def test_update_mixin_get_attrs(self):
class M1(UpdateMixin, FakeManager):
pass
class M2(UpdateMixin, FakeManager):
- _create_attrs = (('foo',), ('bar', 'baz'))
- _update_attrs = (('foo',), ('bam', ))
+ _create_attrs = (("foo",), ("bar", "baz"))
+ _update_attrs = (("foo",), ("bam",))
mgr = M1(self.gl)
required, optional = mgr.get_update_attrs()
@@ -331,70 +335,71 @@ class TestMixinMethods(unittest.TestCase):
mgr = M2(self.gl)
required, optional = mgr.get_update_attrs()
- self.assertIn('foo', required)
- self.assertIn('bam', optional)
- self.assertNotIn('bar', optional)
- self.assertNotIn('baz', optional)
+ self.assertIn("foo", required)
+ self.assertIn("bam", optional)
+ self.assertNotIn("bar", optional)
+ self.assertNotIn("baz", optional)
def test_update_mixin_missing_attrs(self):
class M(UpdateMixin, FakeManager):
- _update_attrs = (('foo',), ('bar', 'baz'))
+ _update_attrs = (("foo",), ("bar", "baz"))
mgr = M(self.gl)
- data = {'foo': 'bar', 'baz': 'blah'}
+ data = {"foo": "bar", "baz": "blah"}
mgr._check_missing_update_attrs(data)
- data = {'baz': 'blah'}
+ data = {"baz": "blah"}
with self.assertRaises(AttributeError) as error:
mgr._check_missing_update_attrs(data)
- self.assertIn('foo', str(error.exception))
+ self.assertIn("foo", str(error.exception))
def test_update_mixin(self):
class M(UpdateMixin, FakeManager):
- _create_attrs = (('foo',), ('bar', 'baz'))
- _update_attrs = (('foo',), ('bam', ))
+ _create_attrs = (("foo",), ("bar", "baz"))
+ _update_attrs = (("foo",), ("bam",))
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/42',
- method="put")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/tests/42", method="put"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"id": 42, "foo": "baz"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
mgr = M(self.gl)
- server_data = mgr.update(42, {'foo': 'baz'})
+ server_data = mgr.update(42, {"foo": "baz"})
self.assertIsInstance(server_data, dict)
- self.assertEqual(server_data['id'], 42)
- self.assertEqual(server_data['foo'], 'baz')
+ self.assertEqual(server_data["id"], 42)
+ self.assertEqual(server_data["foo"], "baz")
def test_update_mixin_no_id(self):
class M(UpdateMixin, FakeManager):
- _create_attrs = (('foo',), ('bar', 'baz'))
- _update_attrs = (('foo',), ('bam', ))
+ _create_attrs = (("foo",), ("bar", "baz"))
+ _update_attrs = (("foo",), ("bam",))
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests',
- method="put")
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="put")
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"foo": "baz"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
mgr = M(self.gl)
- server_data = mgr.update(new_data={'foo': 'baz'})
+ server_data = mgr.update(new_data={"foo": "baz"})
self.assertIsInstance(server_data, dict)
- self.assertEqual(server_data['foo'], 'baz')
+ self.assertEqual(server_data["foo"], "baz")
def test_delete_mixin(self):
class M(DeleteMixin, FakeManager):
pass
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/42',
- method="delete")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/tests/42", method="delete"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
- content = ''
+ headers = {"Content-Type": "application/json"}
+ content = ""
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
@@ -408,35 +413,37 @@ class TestMixinMethods(unittest.TestCase):
class O(SaveMixin, RESTObject):
pass
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/42',
- method="put")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/tests/42", method="put"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"id": 42, "foo": "baz"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
mgr = M(self.gl)
- obj = O(mgr, {'id': 42, 'foo': 'bar'})
- obj.foo = 'baz'
+ obj = O(mgr, {"id": 42, "foo": "bar"})
+ obj.foo = "baz"
obj.save()
- self.assertEqual(obj._attrs['foo'], 'baz')
+ self.assertEqual(obj._attrs["foo"], "baz")
self.assertDictEqual(obj._updated_attrs, {})
def test_set_mixin(self):
class M(SetMixin, FakeManager):
pass
- @urlmatch(scheme="http", netloc="localhost", path='/api/v4/tests/foo',
- method="put")
+ @urlmatch(
+ scheme="http", netloc="localhost", path="/api/v4/tests/foo", method="put"
+ )
def resp_cont(url, request):
- headers = {'Content-Type': 'application/json'}
+ headers = {"Content-Type": "application/json"}
content = '{"key": "foo", "value": "bar"}'
return response(200, content, headers, None, 5, request)
with HTTMock(resp_cont):
mgr = M(self.gl)
- obj = mgr.set('foo', 'bar')
+ obj = mgr.set("foo", "bar")
self.assertIsInstance(obj, FakeObject)
- self.assertEqual(obj.key, 'foo')
- self.assertEqual(obj.value, 'bar')
+ self.assertEqual(obj.key, "foo")
+ self.assertEqual(obj.value, "bar")
diff --git a/gitlab/tests/test_types.py b/gitlab/tests/test_types.py
index c04f68f..4ce065e 100644
--- a/gitlab/tests/test_types.py
+++ b/gitlab/tests/test_types.py
@@ -25,13 +25,13 @@ from gitlab import types
class TestGitlabAttribute(unittest.TestCase):
def test_all(self):
- o = types.GitlabAttribute('whatever')
- self.assertEqual('whatever', o.get())
+ o = types.GitlabAttribute("whatever")
+ self.assertEqual("whatever", o.get())
- o.set_from_cli('whatever2')
- self.assertEqual('whatever2', o.get())
+ o.set_from_cli("whatever2")
+ self.assertEqual("whatever2", o.get())
- self.assertEqual('whatever2', o.get_for_api())
+ self.assertEqual("whatever2", o.get_for_api())
o = types.GitlabAttribute()
self.assertEqual(None, o._value)
@@ -40,27 +40,27 @@ class TestGitlabAttribute(unittest.TestCase):
class TestListAttribute(unittest.TestCase):
def test_list_input(self):
o = types.ListAttribute()
- o.set_from_cli('foo,bar,baz')
- self.assertEqual(['foo', 'bar', 'baz'], o.get())
+ o.set_from_cli("foo,bar,baz")
+ self.assertEqual(["foo", "bar", "baz"], o.get())
- o.set_from_cli('foo')
- self.assertEqual(['foo'], o.get())
+ o.set_from_cli("foo")
+ self.assertEqual(["foo"], o.get())
def test_empty_input(self):
o = types.ListAttribute()
- o.set_from_cli('')
+ o.set_from_cli("")
self.assertEqual([], o.get())
- o.set_from_cli(' ')
+ o.set_from_cli(" ")
self.assertEqual([], o.get())
def test_get_for_api(self):
o = types.ListAttribute()
- o.set_from_cli('foo,bar,baz')
- self.assertEqual('foo,bar,baz', o.get_for_api())
+ o.set_from_cli("foo,bar,baz")
+ self.assertEqual("foo,bar,baz", o.get_for_api())
class TestLowercaseStringAttribute(unittest.TestCase):
def test_get_for_api(self):
- o = types.LowercaseStringAttribute('FOO')
- self.assertEqual('foo', o.get_for_api())
+ o = types.LowercaseStringAttribute("FOO")
+ self.assertEqual("foo", o.get_for_api())
diff --git a/gitlab/types.py b/gitlab/types.py
index b32409f..525dc30 100644
--- a/gitlab/types.py
+++ b/gitlab/types.py
@@ -35,7 +35,7 @@ class ListAttribute(GitlabAttribute):
if not cli_value.strip():
self._value = []
else:
- self._value = [item.strip() for item in cli_value.split(',')]
+ self._value = [item.strip() for item in cli_value.split(",")]
def get_for_api(self):
return ",".join(self._value)
@@ -53,4 +53,4 @@ class FileAttribute(GitlabAttribute):
class ImageAttribute(FileAttribute):
def get_file_name(self, attr_name=None):
- return '%s.png' % attr_name if attr_name else 'image.png'
+ return "%s.png" % attr_name if attr_name else "image.png"
diff --git a/gitlab/utils.py b/gitlab/utils.py
index 49e2c88..6b43800 100644
--- a/gitlab/utils.py
+++ b/gitlab/utils.py
@@ -42,12 +42,12 @@ def copy_dict(dest, src):
# custom_attributes: {'foo', 'bar'} =>
# "custom_attributes['foo']": "bar"
for dict_k, dict_v in v.items():
- dest['%s[%s]' % (k, dict_k)] = dict_v
+ dest["%s[%s]" % (k, dict_k)] = dict_v
else:
dest[k] = v
def sanitized_url(url):
parsed = six.moves.urllib.parse.urlparse(url)
- new_path = parsed.path.replace('.', '%2E')
+ new_path = parsed.path.replace(".", "%2E")
return parsed._replace(path=new_path).geturl()
diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py
index 242874d..f0ed199 100644
--- a/gitlab/v4/cli.py
+++ b/gitlab/v4/cli.py
@@ -33,12 +33,11 @@ class GitlabCLI(object):
def __init__(self, gl, what, action, args):
self.cls_name = cli.what_to_cls(what)
self.cls = gitlab.v4.objects.__dict__[self.cls_name]
- self.what = what.replace('-', '_')
+ self.what = what.replace("-", "_")
self.action = action.lower()
self.gl = gl
self.args = args
- self.mgr_cls = getattr(gitlab.v4.objects,
- self.cls.__name__ + 'Manager')
+ self.mgr_cls = getattr(gitlab.v4.objects, self.cls.__name__ + "Manager")
# We could do something smart, like splitting the manager name to find
# parents, build the chain of managers to get to the final object.
# Instead we do something ugly and efficient: interpolate variables in
@@ -46,7 +45,7 @@ class GitlabCLI(object):
self.mgr_cls._path = self.mgr_cls._path % self.args
self.mgr = self.mgr_cls(gl)
- types = getattr(self.mgr_cls, '_types', {})
+ types = getattr(self.mgr_cls, "_types", {})
if types:
for attr_name, type_cls in types.items():
if attr_name in self.args.keys():
@@ -56,12 +55,12 @@ class GitlabCLI(object):
def __call__(self):
# Check for a method that matches object + action
- method = 'do_%s_%s' % (self.what, self.action)
+ method = "do_%s_%s" % (self.what, self.action)
if hasattr(self, method):
return getattr(self, method)()
# Fallback to standard actions (get, list, create, ...)
- method = 'do_%s' % self.action
+ method = "do_%s" % self.action
if hasattr(self, method):
return getattr(self, method)()
@@ -74,23 +73,22 @@ class GitlabCLI(object):
# Get the object (lazy), then act
if in_obj:
data = {}
- if hasattr(self.mgr, '_from_parent_attrs'):
+ if hasattr(self.mgr, "_from_parent_attrs"):
for k in self.mgr._from_parent_attrs:
data[k] = self.args[k]
if gitlab.mixins.GetWithoutIdMixin not in inspect.getmro(self.cls):
data[self.cls._id_attr] = self.args.pop(self.cls._id_attr)
o = self.cls(self.mgr, data)
- method_name = self.action.replace('-', '_')
+ method_name = self.action.replace("-", "_")
return getattr(o, method_name)(**self.args)
else:
return getattr(self.mgr, self.action)(**self.args)
def do_project_export_download(self):
try:
- project = self.gl.projects.get(int(self.args['project_id']),
- lazy=True)
+ project = self.gl.projects.get(int(self.args["project_id"]), lazy=True)
data = project.exports.get().download()
- if hasattr(sys.stdout, 'buffer'):
+ if hasattr(sys.stdout, "buffer"):
# python3
sys.stdout.buffer.write(data)
else:
@@ -139,121 +137,163 @@ class GitlabCLI(object):
def _populate_sub_parser_by_class(cls, sub_parser):
- mgr_cls_name = cls.__name__ + 'Manager'
+ mgr_cls_name = cls.__name__ + "Manager"
mgr_cls = getattr(gitlab.v4.objects, mgr_cls_name)
- for action_name in ['list', 'get', 'create', 'update', 'delete']:
+ for action_name in ["list", "get", "create", "update", "delete"]:
if not hasattr(mgr_cls, action_name):
continue
sub_parser_action = sub_parser.add_parser(action_name)
sub_parser_action.add_argument("--sudo", required=False)
- if hasattr(mgr_cls, '_from_parent_attrs'):
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in mgr_cls._from_parent_attrs]
+ if hasattr(mgr_cls, "_from_parent_attrs"):
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=True
+ )
+ for x in mgr_cls._from_parent_attrs
+ ]
if action_name == "list":
- if hasattr(mgr_cls, '_list_filters'):
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in mgr_cls._list_filters]
+ if hasattr(mgr_cls, "_list_filters"):
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=False
+ )
+ for x in mgr_cls._list_filters
+ ]
sub_parser_action.add_argument("--page", required=False)
sub_parser_action.add_argument("--per-page", required=False)
- sub_parser_action.add_argument("--all", required=False,
- action='store_true')
+ sub_parser_action.add_argument("--all", required=False, action="store_true")
- if action_name == 'delete':
+ if action_name == "delete":
if cls._id_attr is not None:
- id_attr = cls._id_attr.replace('_', '-')
+ id_attr = cls._id_attr.replace("_", "-")
sub_parser_action.add_argument("--%s" % id_attr, required=True)
if action_name == "get":
if gitlab.mixins.GetWithoutIdMixin not in inspect.getmro(cls):
if cls._id_attr is not None:
- id_attr = cls._id_attr.replace('_', '-')
- sub_parser_action.add_argument("--%s" % id_attr,
- required=True)
+ id_attr = cls._id_attr.replace("_", "-")
+ sub_parser_action.add_argument("--%s" % id_attr, required=True)
- if hasattr(mgr_cls, '_optional_get_attrs'):
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in mgr_cls._optional_get_attrs]
+ if hasattr(mgr_cls, "_optional_get_attrs"):
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=False
+ )
+ for x in mgr_cls._optional_get_attrs
+ ]
if action_name == "create":
- if hasattr(mgr_cls, '_create_attrs'):
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in mgr_cls._create_attrs[0]]
-
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in mgr_cls._create_attrs[1]]
+ if hasattr(mgr_cls, "_create_attrs"):
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=True
+ )
+ for x in mgr_cls._create_attrs[0]
+ ]
+
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=False
+ )
+ for x in mgr_cls._create_attrs[1]
+ ]
if action_name == "update":
if cls._id_attr is not None:
- id_attr = cls._id_attr.replace('_', '-')
- sub_parser_action.add_argument("--%s" % id_attr,
- required=True)
-
- if hasattr(mgr_cls, '_update_attrs'):
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in mgr_cls._update_attrs[0] if x != cls._id_attr]
+ id_attr = cls._id_attr.replace("_", "-")
+ sub_parser_action.add_argument("--%s" % id_attr, required=True)
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in mgr_cls._update_attrs[1] if x != cls._id_attr]
+ if hasattr(mgr_cls, "_update_attrs"):
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=True
+ )
+ for x in mgr_cls._update_attrs[0]
+ if x != cls._id_attr
+ ]
+
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=False
+ )
+ for x in mgr_cls._update_attrs[1]
+ if x != cls._id_attr
+ ]
if cls.__name__ in cli.custom_actions:
name = cls.__name__
for action_name in cli.custom_actions[name]:
sub_parser_action = sub_parser.add_parser(action_name)
# Get the attributes for URL/path construction
- if hasattr(mgr_cls, '_from_parent_attrs'):
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in mgr_cls._from_parent_attrs]
+ if hasattr(mgr_cls, "_from_parent_attrs"):
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=True
+ )
+ for x in mgr_cls._from_parent_attrs
+ ]
sub_parser_action.add_argument("--sudo", required=False)
# We need to get the object somehow
if gitlab.mixins.GetWithoutIdMixin not in inspect.getmro(cls):
if cls._id_attr is not None:
- id_attr = cls._id_attr.replace('_', '-')
- sub_parser_action.add_argument("--%s" % id_attr,
- required=True)
+ id_attr = cls._id_attr.replace("_", "-")
+ sub_parser_action.add_argument("--%s" % id_attr, required=True)
required, optional, dummy = cli.custom_actions[name][action_name]
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in required if x != cls._id_attr]
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in optional if x != cls._id_attr]
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=True
+ )
+ for x in required
+ if x != cls._id_attr
+ ]
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=False
+ )
+ for x in optional
+ if x != cls._id_attr
+ ]
if mgr_cls.__name__ in cli.custom_actions:
name = mgr_cls.__name__
for action_name in cli.custom_actions[name]:
sub_parser_action = sub_parser.add_parser(action_name)
- if hasattr(mgr_cls, '_from_parent_attrs'):
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in mgr_cls._from_parent_attrs]
+ if hasattr(mgr_cls, "_from_parent_attrs"):
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=True
+ )
+ for x in mgr_cls._from_parent_attrs
+ ]
sub_parser_action.add_argument("--sudo", required=False)
required, optional, dummy = cli.custom_actions[name][action_name]
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in required if x != cls._id_attr]
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in optional if x != cls._id_attr]
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=True
+ )
+ for x in required
+ if x != cls._id_attr
+ ]
+ [
+ sub_parser_action.add_argument(
+ "--%s" % x.replace("_", "-"), required=False
+ )
+ for x in optional
+ if x != cls._id_attr
+ ]
def extend_parser(parser):
- subparsers = parser.add_subparsers(title='object', dest='what',
- help="Object to manipulate.")
+ subparsers = parser.add_subparsers(
+ title="object", dest="what", help="Object to manipulate."
+ )
subparsers.required = True
# populate argparse for all Gitlab Object
@@ -272,8 +312,8 @@ def extend_parser(parser):
object_group = subparsers.add_parser(arg_name)
object_subparsers = object_group.add_subparsers(
- title='action',
- dest='action', help="Action to execute.")
+ title="action", dest="action", help="Action to execute."
+ )
_populate_sub_parser_by_class(cls, object_subparsers)
object_subparsers.required = True
@@ -285,18 +325,19 @@ def get_dict(obj, fields):
return obj
if fields:
- return {k: v for k, v in obj.attributes.items()
- if k in fields}
+ return {k: v for k, v in obj.attributes.items() if k in fields}
return obj.attributes
class JSONPrinter(object):
def display(self, d, **kwargs):
import json # noqa
+
print(json.dumps(d))
def display_list(self, data, fields, **kwargs):
import json # noqa
+
print(json.dumps([get_dict(obj, fields) for obj in data]))
@@ -304,39 +345,47 @@ class YAMLPrinter(object):
def display(self, d, **kwargs):
try:
import yaml # noqa
+
print(yaml.safe_dump(d, default_flow_style=False))
except ImportError:
- exit("PyYaml is not installed.\n"
- "Install it with `pip install PyYaml` "
- "to use the yaml output feature")
+ exit(
+ "PyYaml is not installed.\n"
+ "Install it with `pip install PyYaml` "
+ "to use the yaml output feature"
+ )
def display_list(self, data, fields, **kwargs):
try:
import yaml # noqa
- print(yaml.safe_dump(
- [get_dict(obj, fields) for obj in data],
- default_flow_style=False))
+
+ print(
+ yaml.safe_dump(
+ [get_dict(obj, fields) for obj in data], default_flow_style=False
+ )
+ )
except ImportError:
- exit("PyYaml is not installed.\n"
- "Install it with `pip install PyYaml` "
- "to use the yaml output feature")
+ exit(
+ "PyYaml is not installed.\n"
+ "Install it with `pip install PyYaml` "
+ "to use the yaml output feature"
+ )
class LegacyPrinter(object):
def display(self, d, **kwargs):
- verbose = kwargs.get('verbose', False)
- padding = kwargs.get('padding', 0)
- obj = kwargs.get('obj')
+ verbose = kwargs.get("verbose", False)
+ padding = kwargs.get("padding", 0)
+ obj = kwargs.get("obj")
def display_dict(d, padding):
for k in sorted(d.keys()):
v = d[k]
if isinstance(v, dict):
- print('%s%s:' % (' ' * padding, k.replace('_', '-')))
+ print("%s%s:" % (" " * padding, k.replace("_", "-")))
new_padding = padding + 2
self.display(v, verbose=True, padding=new_padding, obj=v)
continue
- print('%s%s: %s' % (' ' * padding, k.replace('_', '-'), v))
+ print("%s%s: %s" % (" " * padding, k.replace("_", "-"), v))
if verbose:
if isinstance(obj, dict):
@@ -346,7 +395,7 @@ class LegacyPrinter(object):
# not a dict, we assume it's a RESTObject
if obj._id_attr:
id = getattr(obj, obj._id_attr, None)
- print('%s: %s' % (obj._id_attr, id))
+ print("%s: %s" % (obj._id_attr, id))
attrs = obj.attributes
if obj._id_attr:
attrs.pop(obj._id_attr)
@@ -355,33 +404,29 @@ class LegacyPrinter(object):
else:
if obj._id_attr:
id = getattr(obj, obj._id_attr)
- print('%s: %s' % (obj._id_attr.replace('_', '-'), id))
- if hasattr(obj, '_short_print_attr'):
+ print("%s: %s" % (obj._id_attr.replace("_", "-"), id))
+ if hasattr(obj, "_short_print_attr"):
value = getattr(obj, obj._short_print_attr)
- value = value.replace('\r', '').replace('\n', ' ')
+ value = value.replace("\r", "").replace("\n", " ")
# If the attribute is a note (ProjectCommitComment) then we do
# some modifications to fit everything on one line
- line = '%s: %s' % (obj._short_print_attr, value)
+ line = "%s: %s" % (obj._short_print_attr, value)
# ellipsize long lines (comments)
if len(line) > 79:
- line = line[:76] + '...'
+ line = line[:76] + "..."
print(line)
def display_list(self, data, fields, **kwargs):
- verbose = kwargs.get('verbose', False)
+ verbose = kwargs.get("verbose", False)
for obj in data:
if isinstance(obj, gitlab.base.RESTObject):
self.display(get_dict(obj, fields), verbose=verbose, obj=obj)
else:
print(obj)
- print('')
+ print("")
-PRINTERS = {
- 'json': JSONPrinter,
- 'legacy': LegacyPrinter,
- 'yaml': YAMLPrinter,
-}
+PRINTERS = {"json": JSONPrinter, "legacy": LegacyPrinter, "yaml": YAMLPrinter}
def run(gl, what, action, args, verbose, output, fields):
@@ -398,5 +443,5 @@ def run(gl, what, action, args, verbose, output, fields):
printer.display(get_dict(data, fields), verbose=verbose, obj=data)
elif isinstance(data, six.string_types):
print(data)
- elif hasattr(data, 'decode'):
+ elif hasattr(data, "decode"):
print(data.decode())
diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py
index ed559cf..16a3da8 100644
--- a/gitlab/v4/objects.py
+++ b/gitlab/v4/objects.py
@@ -26,9 +26,9 @@ from gitlab.mixins import * # noqa
from gitlab import types
from gitlab import utils
-VISIBILITY_PRIVATE = 'private'
-VISIBILITY_INTERNAL = 'internal'
-VISIBILITY_PUBLIC = 'public'
+VISIBILITY_PRIVATE = "private"
+VISIBILITY_INTERNAL = "internal"
+VISIBILITY_PUBLIC = "public"
ACCESS_GUEST = 10
ACCESS_REPORTER = 20
@@ -44,7 +44,7 @@ class SidekiqManager(RESTManager):
for the sidekiq metrics API.
"""
- @cli.register_custom_action('SidekiqManager')
+ @cli.register_custom_action("SidekiqManager")
@exc.on_http_error(exc.GitlabGetError)
def queue_metrics(self, **kwargs):
"""Return the registred queues information.
@@ -59,9 +59,9 @@ class SidekiqManager(RESTManager):
Returns:
dict: Information about the Sidekiq queues
"""
- return self.gitlab.http_get('/sidekiq/queue_metrics', **kwargs)
+ return self.gitlab.http_get("/sidekiq/queue_metrics", **kwargs)
- @cli.register_custom_action('SidekiqManager')
+ @cli.register_custom_action("SidekiqManager")
@exc.on_http_error(exc.GitlabGetError)
def process_metrics(self, **kwargs):
"""Return the registred sidekiq workers.
@@ -76,9 +76,9 @@ class SidekiqManager(RESTManager):
Returns:
dict: Information about the register Sidekiq worker
"""
- return self.gitlab.http_get('/sidekiq/process_metrics', **kwargs)
+ return self.gitlab.http_get("/sidekiq/process_metrics", **kwargs)
- @cli.register_custom_action('SidekiqManager')
+ @cli.register_custom_action("SidekiqManager")
@exc.on_http_error(exc.GitlabGetError)
def job_stats(self, **kwargs):
"""Return statistics about the jobs performed.
@@ -93,9 +93,9 @@ class SidekiqManager(RESTManager):
Returns:
dict: Statistics about the Sidekiq jobs performed
"""
- return self.gitlab.http_get('/sidekiq/job_stats', **kwargs)
+ return self.gitlab.http_get("/sidekiq/job_stats", **kwargs)
- @cli.register_custom_action('SidekiqManager')
+ @cli.register_custom_action("SidekiqManager")
@exc.on_http_error(exc.GitlabGetError)
def compound_metrics(self, **kwargs):
"""Return all available metrics and statistics.
@@ -110,49 +110,48 @@ class SidekiqManager(RESTManager):
Returns:
dict: All available Sidekiq metrics and statistics
"""
- return self.gitlab.http_get('/sidekiq/compound_metrics', **kwargs)
+ return self.gitlab.http_get("/sidekiq/compound_metrics", **kwargs)
class Event(RESTObject):
_id_attr = None
- _short_print_attr = 'target_title'
+ _short_print_attr = "target_title"
class EventManager(ListMixin, RESTManager):
- _path = '/events'
+ _path = "/events"
_obj_cls = Event
- _list_filters = ('action', 'target_type', 'before', 'after', 'sort')
+ _list_filters = ("action", "target_type", "before", "after", "sort")
class UserActivities(RESTObject):
- _id_attr = 'username'
+ _id_attr = "username"
class UserActivitiesManager(ListMixin, RESTManager):
- _path = '/user/activities'
+ _path = "/user/activities"
_obj_cls = UserActivities
class UserCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = 'key'
+ _id_attr = "key"
-class UserCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin,
- RESTManager):
- _path = '/users/%(user_id)s/custom_attributes'
+class UserCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
+ _path = "/users/%(user_id)s/custom_attributes"
_obj_cls = UserCustomAttribute
- _from_parent_attrs = {'user_id': 'id'}
+ _from_parent_attrs = {"user_id": "id"}
class UserEmail(ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'email'
+ _short_print_attr = "email"
class UserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = '/users/%(user_id)s/emails'
+ _path = "/users/%(user_id)s/emails"
_obj_cls = UserEmail
- _from_parent_attrs = {'user_id': 'id'}
- _create_attrs = (('email', ), tuple())
+ _from_parent_attrs = {"user_id": "id"}
+ _create_attrs = (("email",), tuple())
class UserEvent(Event):
@@ -160,9 +159,9 @@ class UserEvent(Event):
class UserEventManager(EventManager):
- _path = '/users/%(user_id)s/events'
+ _path = "/users/%(user_id)s/events"
_obj_cls = UserEvent
- _from_parent_attrs = {'user_id': 'id'}
+ _from_parent_attrs = {"user_id": "id"}
class UserGPGKey(ObjectDeleteMixin, RESTObject):
@@ -170,10 +169,10 @@ class UserGPGKey(ObjectDeleteMixin, RESTObject):
class UserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = '/users/%(user_id)s/gpg_keys'
+ _path = "/users/%(user_id)s/gpg_keys"
_obj_cls = UserGPGKey
- _from_parent_attrs = {'user_id': 'id'}
- _create_attrs = (('key',), tuple())
+ _from_parent_attrs = {"user_id": "id"}
+ _create_attrs = (("key",), tuple())
class UserKey(ObjectDeleteMixin, RESTObject):
@@ -181,10 +180,10 @@ class UserKey(ObjectDeleteMixin, RESTObject):
class UserKeyManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = '/users/%(user_id)s/keys'
+ _path = "/users/%(user_id)s/keys"
_obj_cls = UserKey
- _from_parent_attrs = {'user_id': 'id'}
- _create_attrs = (('title', 'key'), tuple())
+ _from_parent_attrs = {"user_id": "id"}
+ _create_attrs = (("title", "key"), tuple())
class UserImpersonationToken(ObjectDeleteMixin, RESTObject):
@@ -192,11 +191,11 @@ class UserImpersonationToken(ObjectDeleteMixin, RESTObject):
class UserImpersonationTokenManager(NoUpdateMixin, RESTManager):
- _path = '/users/%(user_id)s/impersonation_tokens'
+ _path = "/users/%(user_id)s/impersonation_tokens"
_obj_cls = UserImpersonationToken
- _from_parent_attrs = {'user_id': 'id'}
- _create_attrs = (('name', 'scopes'), ('expires_at',))
- _list_filters = ('state',)
+ _from_parent_attrs = {"user_id": "id"}
+ _create_attrs = (("name", "scopes"), ("expires_at",))
+ _list_filters = ("state",)
class UserProject(RESTObject):
@@ -204,19 +203,41 @@ class UserProject(RESTObject):
class UserProjectManager(ListMixin, CreateMixin, RESTManager):
- _path = '/projects/user/%(user_id)s'
+ _path = "/projects/user/%(user_id)s"
_obj_cls = UserProject
- _from_parent_attrs = {'user_id': 'id'}
+ _from_parent_attrs = {"user_id": "id"}
_create_attrs = (
- ('name', ),
- ('default_branch', 'issues_enabled', 'wall_enabled',
- 'merge_requests_enabled', 'wiki_enabled', 'snippets_enabled',
- 'public', 'visibility', 'description', 'builds_enabled',
- 'public_builds', 'import_url', 'only_allow_merge_if_build_succeeds')
+ ("name",),
+ (
+ "default_branch",
+ "issues_enabled",
+ "wall_enabled",
+ "merge_requests_enabled",
+ "wiki_enabled",
+ "snippets_enabled",
+ "public",
+ "visibility",
+ "description",
+ "builds_enabled",
+ "public_builds",
+ "import_url",
+ "only_allow_merge_if_build_succeeds",
+ ),
+ )
+ _list_filters = (
+ "archived",
+ "visibility",
+ "order_by",
+ "sort",
+ "search",
+ "simple",
+ "owned",
+ "membership",
+ "starred",
+ "statistics",
+ "with_issues_enabled",
+ "with_merge_requests_enabled",
)
- _list_filters = ('archived', 'visibility', 'order_by', 'sort', 'search',
- 'simple', 'owned', 'membership', 'starred', 'statistics',
- 'with_issues_enabled', 'with_merge_requests_enabled')
def list(self, **kwargs):
"""Retrieve a list of objects.
@@ -237,23 +258,23 @@ class UserProjectManager(ListMixin, CreateMixin, RESTManager):
GitlabListError: If the server cannot perform the request
"""
- path = '/users/%s/projects' % self._parent.id
+ path = "/users/%s/projects" % self._parent.id
return ListMixin.list(self, path=path, **kwargs)
class User(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'username'
+ _short_print_attr = "username"
_managers = (
- ('customattributes', 'UserCustomAttributeManager'),
- ('emails', 'UserEmailManager'),
- ('events', 'UserEventManager'),
- ('gpgkeys', 'UserGPGKeyManager'),
- ('impersonationtokens', 'UserImpersonationTokenManager'),
- ('keys', 'UserKeyManager'),
- ('projects', 'UserProjectManager'),
+ ("customattributes", "UserCustomAttributeManager"),
+ ("emails", "UserEmailManager"),
+ ("events", "UserEventManager"),
+ ("gpgkeys", "UserGPGKeyManager"),
+ ("impersonationtokens", "UserImpersonationTokenManager"),
+ ("keys", "UserKeyManager"),
+ ("projects", "UserProjectManager"),
)
- @cli.register_custom_action('User')
+ @cli.register_custom_action("User")
@exc.on_http_error(exc.GitlabBlockError)
def block(self, **kwargs):
"""Block the user.
@@ -268,13 +289,13 @@ class User(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
bool: Whether the user status has been changed
"""
- path = '/users/%s/block' % self.id
+ path = "/users/%s/block" % self.id
server_data = self.manager.gitlab.http_post(path, **kwargs)
if server_data is True:
- self._attrs['state'] = 'blocked'
+ self._attrs["state"] = "blocked"
return server_data
- @cli.register_custom_action('User')
+ @cli.register_custom_action("User")
@exc.on_http_error(exc.GitlabUnblockError)
def unblock(self, **kwargs):
"""Unblock the user.
@@ -289,84 +310,118 @@ class User(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
bool: Whether the user status has been changed
"""
- path = '/users/%s/unblock' % self.id
+ path = "/users/%s/unblock" % self.id
server_data = self.manager.gitlab.http_post(path, **kwargs)
if server_data is True:
- self._attrs['state'] = 'active'
+ self._attrs["state"] = "active"
return server_data
class UserManager(CRUDMixin, RESTManager):
- _path = '/users'
+ _path = "/users"
_obj_cls = User
- _list_filters = ('active', 'blocked', 'username', 'extern_uid', 'provider',
- 'external', 'search', 'custom_attributes')
+ _list_filters = (
+ "active",
+ "blocked",
+ "username",
+ "extern_uid",
+ "provider",
+ "external",
+ "search",
+ "custom_attributes",
+ )
_create_attrs = (
tuple(),
- ('email', 'username', 'name', 'password', 'reset_password', 'skype',
- 'linkedin', 'twitter', 'projects_limit', 'extern_uid', 'provider',
- 'bio', 'admin', 'can_create_group', 'website_url',
- 'skip_confirmation', 'external', 'organization', 'location', 'avatar')
+ (
+ "email",
+ "username",
+ "name",
+ "password",
+ "reset_password",
+ "skype",
+ "linkedin",
+ "twitter",
+ "projects_limit",
+ "extern_uid",
+ "provider",
+ "bio",
+ "admin",
+ "can_create_group",
+ "website_url",
+ "skip_confirmation",
+ "external",
+ "organization",
+ "location",
+ "avatar",
+ ),
)
_update_attrs = (
- ('email', 'username', 'name'),
- ('password', 'skype', 'linkedin', 'twitter', 'projects_limit',
- 'extern_uid', 'provider', 'bio', 'admin', 'can_create_group',
- 'website_url', 'skip_confirmation', 'external', 'organization',
- 'location', 'avatar')
+ ("email", "username", "name"),
+ (
+ "password",
+ "skype",
+ "linkedin",
+ "twitter",
+ "projects_limit",
+ "extern_uid",
+ "provider",
+ "bio",
+ "admin",
+ "can_create_group",
+ "website_url",
+ "skip_confirmation",
+ "external",
+ "organization",
+ "location",
+ "avatar",
+ ),
)
- _types = {
- 'confirm': types.LowercaseStringAttribute,
- 'avatar': types.ImageAttribute,
- }
+ _types = {"confirm": types.LowercaseStringAttribute, "avatar": types.ImageAttribute}
class CurrentUserEmail(ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'email'
+ _short_print_attr = "email"
-class CurrentUserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin,
- RESTManager):
- _path = '/user/emails'
+class CurrentUserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
+ _path = "/user/emails"
_obj_cls = CurrentUserEmail
- _create_attrs = (('email', ), tuple())
+ _create_attrs = (("email",), tuple())
class CurrentUserGPGKey(ObjectDeleteMixin, RESTObject):
pass
-class CurrentUserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin,
- RESTManager):
- _path = '/user/gpg_keys'
+class CurrentUserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
+ _path = "/user/gpg_keys"
_obj_cls = CurrentUserGPGKey
- _create_attrs = (('key',), tuple())
+ _create_attrs = (("key",), tuple())
class CurrentUserKey(ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'title'
+ _short_print_attr = "title"
-class CurrentUserKeyManager(RetrieveMixin, CreateMixin, DeleteMixin,
- RESTManager):
- _path = '/user/keys'
+class CurrentUserKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
+ _path = "/user/keys"
_obj_cls = CurrentUserKey
- _create_attrs = (('title', 'key'), tuple())
+ _create_attrs = (("title", "key"), tuple())
class CurrentUser(RESTObject):
_id_attr = None
- _short_print_attr = 'username'
+ _short_print_attr = "username"
_managers = (
- ('emails', 'CurrentUserEmailManager'),
- ('gpgkeys', 'CurrentUserGPGKeyManager'),
- ('keys', 'CurrentUserKeyManager'),
+ ("emails", "CurrentUserEmailManager"),
+ ("gpgkeys", "CurrentUserGPGKeyManager"),
+ ("keys", "CurrentUserKeyManager"),
)
class CurrentUserManager(GetWithoutIdMixin, RESTManager):
- _path = '/user'
+ _path = "/user"
_obj_cls = CurrentUser
@@ -375,52 +430,103 @@ class ApplicationSettings(SaveMixin, RESTObject):
class ApplicationSettingsManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = '/application/settings'
+ _path = "/application/settings"
_obj_cls = ApplicationSettings
_update_attrs = (
tuple(),
- ('admin_notification_email', 'after_sign_out_path',
- 'after_sign_up_text', 'akismet_api_key', 'akismet_enabled',
- 'circuitbreaker_access_retries', 'circuitbreaker_check_interval',
- 'circuitbreaker_failure_count_threshold',
- 'circuitbreaker_failure_reset_time', 'circuitbreaker_storage_timeout',
- 'clientside_sentry_dsn', 'clientside_sentry_enabled',
- 'container_registry_token_expire_delay',
- 'default_artifacts_expire_in', 'default_branch_protection',
- 'default_group_visibility', 'default_project_visibility',
- 'default_projects_limit', 'default_snippet_visibility',
- 'disabled_oauth_sign_in_sources', 'domain_blacklist_enabled',
- 'domain_blacklist', 'domain_whitelist', 'dsa_key_restriction',
- 'ecdsa_key_restriction', 'ed25519_key_restriction',
- 'email_author_in_body', 'enabled_git_access_protocol',
- 'gravatar_enabled', 'help_page_hide_commercial_content',
- 'help_page_support_url', 'home_page_url',
- 'housekeeping_bitmaps_enabled', 'housekeeping_enabled',
- 'housekeeping_full_repack_period', 'housekeeping_gc_period',
- 'housekeeping_incremental_repack_period', 'html_emails_enabled',
- 'import_sources', 'koding_enabled', 'koding_url',
- 'max_artifacts_size', 'max_attachment_size', 'max_pages_size',
- 'metrics_enabled', 'metrics_host', 'metrics_method_call_threshold',
- 'metrics_packet_size', 'metrics_pool_size', 'metrics_port',
- 'metrics_sample_interval', 'metrics_timeout',
- 'password_authentication_enabled_for_web',
- 'password_authentication_enabled_for_git',
- 'performance_bar_allowed_group_id', 'performance_bar_enabled',
- 'plantuml_enabled', 'plantuml_url', 'polling_interval_multiplier',
- 'project_export_enabled', 'prometheus_metrics_enabled',
- 'recaptcha_enabled', 'recaptcha_private_key', 'recaptcha_site_key',
- 'repository_checks_enabled', 'repository_storages',
- 'require_two_factor_authentication', 'restricted_visibility_levels',
- 'rsa_key_restriction', 'send_user_confirmation_email', 'sentry_dsn',
- 'sentry_enabled', 'session_expire_delay', 'shared_runners_enabled',
- 'shared_runners_text', 'sidekiq_throttling_enabled',
- 'sidekiq_throttling_factor', 'sidekiq_throttling_queues',
- 'sign_in_text', 'signup_enabled', 'terminal_max_session_time',
- 'two_factor_grace_period', 'unique_ips_limit_enabled',
- 'unique_ips_limit_per_user', 'unique_ips_limit_time_window',
- 'usage_ping_enabled', 'user_default_external',
- 'user_oauth_applications', 'version_check_enabled', 'enforce_terms',
- 'terms')
+ (
+ "admin_notification_email",
+ "after_sign_out_path",
+ "after_sign_up_text",
+ "akismet_api_key",
+ "akismet_enabled",
+ "circuitbreaker_access_retries",
+ "circuitbreaker_check_interval",
+ "circuitbreaker_failure_count_threshold",
+ "circuitbreaker_failure_reset_time",
+ "circuitbreaker_storage_timeout",
+ "clientside_sentry_dsn",
+ "clientside_sentry_enabled",
+ "container_registry_token_expire_delay",
+ "default_artifacts_expire_in",
+ "default_branch_protection",
+ "default_group_visibility",
+ "default_project_visibility",
+ "default_projects_limit",
+ "default_snippet_visibility",
+ "disabled_oauth_sign_in_sources",
+ "domain_blacklist_enabled",
+ "domain_blacklist",
+ "domain_whitelist",
+ "dsa_key_restriction",
+ "ecdsa_key_restriction",
+ "ed25519_key_restriction",
+ "email_author_in_body",
+ "enabled_git_access_protocol",
+ "gravatar_enabled",
+ "help_page_hide_commercial_content",
+ "help_page_support_url",
+ "home_page_url",
+ "housekeeping_bitmaps_enabled",
+ "housekeeping_enabled",
+ "housekeeping_full_repack_period",
+ "housekeeping_gc_period",
+ "housekeeping_incremental_repack_period",
+ "html_emails_enabled",
+ "import_sources",
+ "koding_enabled",
+ "koding_url",
+ "max_artifacts_size",
+ "max_attachment_size",
+ "max_pages_size",
+ "metrics_enabled",
+ "metrics_host",
+ "metrics_method_call_threshold",
+ "metrics_packet_size",
+ "metrics_pool_size",
+ "metrics_port",
+ "metrics_sample_interval",
+ "metrics_timeout",
+ "password_authentication_enabled_for_web",
+ "password_authentication_enabled_for_git",
+ "performance_bar_allowed_group_id",
+ "performance_bar_enabled",
+ "plantuml_enabled",
+ "plantuml_url",
+ "polling_interval_multiplier",
+ "project_export_enabled",
+ "prometheus_metrics_enabled",
+ "recaptcha_enabled",
+ "recaptcha_private_key",
+ "recaptcha_site_key",
+ "repository_checks_enabled",
+ "repository_storages",
+ "require_two_factor_authentication",
+ "restricted_visibility_levels",
+ "rsa_key_restriction",
+ "send_user_confirmation_email",
+ "sentry_dsn",
+ "sentry_enabled",
+ "session_expire_delay",
+ "shared_runners_enabled",
+ "shared_runners_text",
+ "sidekiq_throttling_enabled",
+ "sidekiq_throttling_factor",
+ "sidekiq_throttling_queues",
+ "sign_in_text",
+ "signup_enabled",
+ "terminal_max_session_time",
+ "two_factor_grace_period",
+ "unique_ips_limit_enabled",
+ "unique_ips_limit_per_user",
+ "unique_ips_limit_time_window",
+ "usage_ping_enabled",
+ "user_default_external",
+ "user_oauth_applications",
+ "version_check_enabled",
+ "enforce_terms",
+ "terms",
+ ),
)
@exc.on_http_error(exc.GitlabUpdateError)
@@ -441,8 +547,8 @@ class ApplicationSettingsManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
"""
data = new_data.copy()
- if 'domain_whitelist' in data and data['domain_whitelist'] is None:
- data.pop('domain_whitelist')
+ if "domain_whitelist" in data and data["domain_whitelist"] is None:
+ data.pop("domain_whitelist")
super(ApplicationSettingsManager, self).update(id, data, **kwargs)
@@ -451,12 +557,11 @@ class BroadcastMessage(SaveMixin, ObjectDeleteMixin, RESTObject):
class BroadcastMessageManager(CRUDMixin, RESTManager):
- _path = '/broadcast_messages'
+ _path = "/broadcast_messages"
_obj_cls = BroadcastMessage
- _create_attrs = (('message', ), ('starts_at', 'ends_at', 'color', 'font'))
- _update_attrs = (tuple(), ('message', 'starts_at', 'ends_at', 'color',
- 'font'))
+ _create_attrs = (("message",), ("starts_at", "ends_at", "color", "font"))
+ _update_attrs = (tuple(), ("message", "starts_at", "ends_at", "color", "font"))
class DeployKey(RESTObject):
@@ -464,7 +569,7 @@ class DeployKey(RESTObject):
class DeployKeyManager(ListMixin, RESTManager):
- _path = '/deploy_keys'
+ _path = "/deploy_keys"
_obj_cls = DeployKey
@@ -473,33 +578,43 @@ class NotificationSettings(SaveMixin, RESTObject):
class NotificationSettingsManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = '/notification_settings'
+ _path = "/notification_settings"
_obj_cls = NotificationSettings
_update_attrs = (
tuple(),
- ('level', 'notification_email', 'new_note', 'new_issue',
- 'reopen_issue', 'close_issue', 'reassign_issue', 'new_merge_request',
- 'reopen_merge_request', 'close_merge_request',
- 'reassign_merge_request', 'merge_merge_request')
+ (
+ "level",
+ "notification_email",
+ "new_note",
+ "new_issue",
+ "reopen_issue",
+ "close_issue",
+ "reassign_issue",
+ "new_merge_request",
+ "reopen_merge_request",
+ "close_merge_request",
+ "reassign_merge_request",
+ "merge_merge_request",
+ ),
)
class Dockerfile(RESTObject):
- _id_attr = 'name'
+ _id_attr = "name"
class DockerfileManager(RetrieveMixin, RESTManager):
- _path = '/templates/dockerfiles'
+ _path = "/templates/dockerfiles"
_obj_cls = Dockerfile
class Feature(ObjectDeleteMixin, RESTObject):
- _id_attr = 'name'
+ _id_attr = "name"
class FeatureManager(ListMixin, DeleteMixin, RESTManager):
- _path = '/features/'
+ _path = "/features/"
_obj_cls = Feature
@exc.on_http_error(exc.GitlabSetError)
@@ -520,27 +635,27 @@ class FeatureManager(ListMixin, DeleteMixin, RESTManager):
Returns:
obj: The created/updated attribute
"""
- path = '%s/%s' % (self.path, name.replace('/', '%2F'))
- data = {'value': value, 'feature_group': feature_group, 'user': user}
+ path = "%s/%s" % (self.path, name.replace("/", "%2F"))
+ data = {"value": value, "feature_group": feature_group, "user": user}
server_data = self.gitlab.http_post(path, post_data=data, **kwargs)
return self._obj_cls(self, server_data)
class Gitignore(RESTObject):
- _id_attr = 'name'
+ _id_attr = "name"
class GitignoreManager(RetrieveMixin, RESTManager):
- _path = '/templates/gitignores'
+ _path = "/templates/gitignores"
_obj_cls = Gitignore
class Gitlabciyml(RESTObject):
- _id_attr = 'name'
+ _id_attr = "name"
class GitlabciymlManager(RetrieveMixin, RESTManager):
- _path = '/templates/gitlab_ci_ymls'
+ _path = "/templates/gitlab_ci_ymls"
_obj_cls = Gitlabciyml
@@ -548,11 +663,10 @@ class GroupAccessRequest(AccessRequestMixin, ObjectDeleteMixin, RESTObject):
pass
-class GroupAccessRequestManager(ListMixin, CreateMixin, DeleteMixin,
- RESTManager):
- _path = '/groups/%(group_id)s/access_requests'
+class GroupAccessRequestManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
+ _path = "/groups/%(group_id)s/access_requests"
_obj_cls = GroupAccessRequest
- _from_parent_attrs = {'group_id': 'id'}
+ _from_parent_attrs = {"group_id": "id"}
class GroupBadge(SaveMixin, ObjectDeleteMixin, RESTObject):
@@ -560,11 +674,11 @@ class GroupBadge(SaveMixin, ObjectDeleteMixin, RESTObject):
class GroupBadgeManager(BadgeRenderMixin, CRUDMixin, RESTManager):
- _path = '/groups/%(group_id)s/badges'
+ _path = "/groups/%(group_id)s/badges"
_obj_cls = GroupBadge
- _from_parent_attrs = {'group_id': 'id'}
- _create_attrs = (('link_url', 'image_url'), tuple())
- _update_attrs = (tuple(), ('link_url', 'image_url'))
+ _from_parent_attrs = {"group_id": "id"}
+ _create_attrs = (("link_url", "image_url"), tuple())
+ _update_attrs = (tuple(), ("link_url", "image_url"))
class GroupBoardList(SaveMixin, ObjectDeleteMixin, RESTObject):
@@ -572,38 +686,36 @@ class GroupBoardList(SaveMixin, ObjectDeleteMixin, RESTObject):
class GroupBoardListManager(CRUDMixin, RESTManager):
- _path = '/groups/%(group_id)s/boards/%(board_id)s/lists'
+ _path = "/groups/%(group_id)s/boards/%(board_id)s/lists"
_obj_cls = GroupBoardList
- _from_parent_attrs = {'group_id': 'group_id',
- 'board_id': 'id'}
- _create_attrs = (('label_id', ), tuple())
- _update_attrs = (('position', ), tuple())
+ _from_parent_attrs = {"group_id": "group_id", "board_id": "id"}
+ _create_attrs = (("label_id",), tuple())
+ _update_attrs = (("position",), tuple())
class GroupBoard(ObjectDeleteMixin, RESTObject):
- _managers = (('lists', 'GroupBoardListManager'), )
+ _managers = (("lists", "GroupBoardListManager"),)
class GroupBoardManager(NoUpdateMixin, RESTManager):
- _path = '/groups/%(group_id)s/boards'
+ _path = "/groups/%(group_id)s/boards"
_obj_cls = GroupBoard
- _from_parent_attrs = {'group_id': 'id'}
- _create_attrs = (('name', ), tuple())
+ _from_parent_attrs = {"group_id": "id"}
+ _create_attrs = (("name",), tuple())
class GroupCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = 'key'
+ _id_attr = "key"
-class GroupCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin,
- RESTManager):
- _path = '/groups/%(group_id)s/custom_attributes'
+class GroupCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
+ _path = "/groups/%(group_id)s/custom_attributes"
_obj_cls = GroupCustomAttribute
- _from_parent_attrs = {'group_id': 'id'}
+ _from_parent_attrs = {"group_id": "id"}
class GroupEpicIssue(ObjectDeleteMixin, SaveMixin, RESTObject):
- _id_attr = 'epic_issue_id'
+ _id_attr = "epic_issue_id"
def save(self, **kwargs):
"""Save the changes made to the object to the server.
@@ -627,13 +739,14 @@ class GroupEpicIssue(ObjectDeleteMixin, SaveMixin, RESTObject):
self.manager.update(obj_id, updated_data, **kwargs)
-class GroupEpicIssueManager(ListMixin, CreateMixin, UpdateMixin, DeleteMixin,
- RESTManager):
- _path = '/groups/%(group_id)s/epics/%(epic_iid)s/issues'
+class GroupEpicIssueManager(
+ ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = "/groups/%(group_id)s/epics/%(epic_iid)s/issues"
_obj_cls = GroupEpicIssue
- _from_parent_attrs = {'group_id': 'group_id', 'epic_iid': 'iid'}
- _create_attrs = (('issue_id',), tuple())
- _update_attrs = (tuple(), ('move_before_id', 'move_after_id'))
+ _from_parent_attrs = {"group_id": "group_id", "epic_iid": "iid"}
+ _create_attrs = (("issue_id",), tuple())
+ _update_attrs = (tuple(), ("move_before_id", "move_after_id"))
@exc.on_http_error(exc.GitlabCreateError)
def create(self, data, **kwargs):
@@ -653,12 +766,12 @@ class GroupEpicIssueManager(ListMixin, CreateMixin, UpdateMixin, DeleteMixin,
the data sent by the server
"""
CreateMixin._check_missing_create_attrs(self, data)
- path = '%s/%s' % (self.path, data.pop('issue_id'))
+ path = "%s/%s" % (self.path, data.pop("issue_id"))
server_data = self.gitlab.http_post(path, **kwargs)
# The epic_issue_id attribute doesn't exist when creating the resource,
# but is used everywhere elese. Let's create it to be consistent client
# side
- server_data['epic_issue_id'] = server_data['id']
+ server_data["epic_issue_id"] = server_data["id"]
return self._obj_cls(self, server_data)
@@ -667,29 +780,30 @@ class GroupEpicResourceLabelEvent(RESTObject):
class GroupEpicResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = ('/groups/%(group_id)s/epics/%(epic_id)s/resource_label_events')
+ _path = "/groups/%(group_id)s/epics/%(epic_id)s/resource_label_events"
_obj_cls = GroupEpicResourceLabelEvent
- _from_parent_attrs = {'group_id': 'group_id', 'epic_id': 'id'}
+ _from_parent_attrs = {"group_id": "group_id", "epic_id": "id"}
class GroupEpic(ObjectDeleteMixin, SaveMixin, RESTObject):
- _id_attr = 'iid'
+ _id_attr = "iid"
_managers = (
- ('issues', 'GroupEpicIssueManager'),
- ('resourcelabelevents', 'GroupEpicResourceLabelEventManager'),
+ ("issues", "GroupEpicIssueManager"),
+ ("resourcelabelevents", "GroupEpicResourceLabelEventManager"),
)
class GroupEpicManager(CRUDMixin, RESTManager):
- _path = '/groups/%(group_id)s/epics'
+ _path = "/groups/%(group_id)s/epics"
_obj_cls = GroupEpic
- _from_parent_attrs = {'group_id': 'id'}
- _list_filters = ('author_id', 'labels', 'order_by', 'sort', 'search')
- _create_attrs = (('title',),
- ('labels', 'description', 'start_date', 'end_date'))
- _update_attrs = (tuple(), ('title', 'labels', 'description', 'start_date',
- 'end_date'))
- _types = {'labels': types.ListAttribute}
+ _from_parent_attrs = {"group_id": "id"}
+ _list_filters = ("author_id", "labels", "order_by", "sort", "search")
+ _create_attrs = (("title",), ("labels", "description", "start_date", "end_date"))
+ _update_attrs = (
+ tuple(),
+ ("title", "labels", "description", "start_date", "end_date"),
+ )
+ _types = {"labels": types.ListAttribute}
class GroupIssue(RESTObject):
@@ -697,28 +811,40 @@ class GroupIssue(RESTObject):
class GroupIssueManager(ListMixin, RESTManager):
- _path = '/groups/%(group_id)s/issues'
+ _path = "/groups/%(group_id)s/issues"
_obj_cls = GroupIssue
- _from_parent_attrs = {'group_id': 'id'}
- _list_filters = ('state', 'labels', 'milestone', 'order_by', 'sort',
- 'iids', 'author_id', 'assignee_id', 'my_reaction_emoji',
- 'search', 'created_after', 'created_before',
- 'updated_after', 'updated_before')
- _types = {'labels': types.ListAttribute}
+ _from_parent_attrs = {"group_id": "id"}
+ _list_filters = (
+ "state",
+ "labels",
+ "milestone",
+ "order_by",
+ "sort",
+ "iids",
+ "author_id",
+ "assignee_id",
+ "my_reaction_emoji",
+ "search",
+ "created_after",
+ "created_before",
+ "updated_after",
+ "updated_before",
+ )
+ _types = {"labels": types.ListAttribute}
class GroupMember(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'username'
+ _short_print_attr = "username"
class GroupMemberManager(CRUDMixin, RESTManager):
- _path = '/groups/%(group_id)s/members'
+ _path = "/groups/%(group_id)s/members"
_obj_cls = GroupMember
- _from_parent_attrs = {'group_id': 'id'}
- _create_attrs = (('access_level', 'user_id'), ('expires_at', ))
- _update_attrs = (('access_level', ), ('expires_at', ))
+ _from_parent_attrs = {"group_id": "id"}
+ _create_attrs = (("access_level", "user_id"), ("expires_at",))
+ _update_attrs = (("access_level",), ("expires_at",))
- @cli.register_custom_action('GroupMemberManager')
+ @cli.register_custom_action("GroupMemberManager")
@exc.on_http_error(exc.GitlabListError)
def all(self, **kwargs):
"""List all the members, included inherited ones.
@@ -739,7 +865,7 @@ class GroupMemberManager(CRUDMixin, RESTManager):
RESTObjectList: The list of members
"""
- path = '%s/all' % self.path
+ path = "%s/all" % self.path
obj = self.gitlab.http_list(path, **kwargs)
return [self._obj_cls(self, item) for item in obj]
@@ -749,21 +875,35 @@ class GroupMergeRequest(RESTObject):
class GroupMergeRequestManager(ListMixin, RESTManager):
- _path = '/groups/%(group_id)s/merge_requests'
+ _path = "/groups/%(group_id)s/merge_requests"
_obj_cls = GroupMergeRequest
- _from_parent_attrs = {'group_id': 'id'}
- _list_filters = ('state', 'order_by', 'sort', 'milestone', 'view',
- 'labels', 'created_after', 'created_before',
- 'updated_after', 'updated_before', 'scope', 'author_id',
- 'assignee_id', 'my_reaction_emoji', 'source_branch',
- 'target_branch', 'search')
- _types = {'labels': types.ListAttribute}
+ _from_parent_attrs = {"group_id": "id"}
+ _list_filters = (
+ "state",
+ "order_by",
+ "sort",
+ "milestone",
+ "view",
+ "labels",
+ "created_after",
+ "created_before",
+ "updated_after",
+ "updated_before",
+ "scope",
+ "author_id",
+ "assignee_id",
+ "my_reaction_emoji",
+ "source_branch",
+ "target_branch",
+ "search",
+ )
+ _types = {"labels": types.ListAttribute}
class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'title'
+ _short_print_attr = "title"
- @cli.register_custom_action('GroupMilestone')
+ @cli.register_custom_action("GroupMilestone")
@exc.on_http_error(exc.GitlabListError)
def issues(self, **kwargs):
"""List issues related to this milestone.
@@ -784,15 +924,13 @@ class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
RESTObjectList: The list of issues
"""
- path = '%s/%s/issues' % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False,
- **kwargs)
- manager = GroupIssueManager(self.manager.gitlab,
- parent=self.manager._parent)
+ path = "%s/%s/issues" % (self.manager.path, self.get_id())
+ data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
+ manager = GroupIssueManager(self.manager.gitlab, parent=self.manager._parent)
# FIXME(gpocentek): the computed manager path is not correct
return RESTObjectList(manager, GroupIssue, data_list)
- @cli.register_custom_action('GroupMilestone')
+ @cli.register_custom_action("GroupMilestone")
@exc.on_http_error(exc.GitlabListError)
def merge_requests(self, **kwargs):
"""List the merge requests related to this milestone.
@@ -812,23 +950,23 @@ class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
RESTObjectList: The list of merge requests
"""
- path = '%s/%s/merge_requests' % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False,
- **kwargs)
- manager = GroupIssueManager(self.manager.gitlab,
- parent=self.manager._parent)
+ path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
+ data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
+ manager = GroupIssueManager(self.manager.gitlab, parent=self.manager._parent)
# FIXME(gpocentek): the computed manager path is not correct
return RESTObjectList(manager, GroupMergeRequest, data_list)
class GroupMilestoneManager(CRUDMixin, RESTManager):
- _path = '/groups/%(group_id)s/milestones'
+ _path = "/groups/%(group_id)s/milestones"
_obj_cls = GroupMilestone
- _from_parent_attrs = {'group_id': 'id'}
- _create_attrs = (('title', ), ('description', 'due_date', 'start_date'))
- _update_attrs = (tuple(), ('title', 'description', 'due_date',
- 'start_date', 'state_event'))
- _list_filters = ('iids', 'state', 'search')
+ _from_parent_attrs = {"group_id": "id"}
+ _create_attrs = (("title",), ("description", "due_date", "start_date"))
+ _update_attrs = (
+ tuple(),
+ ("title", "description", "due_date", "start_date", "state_event"),
+ )
+ _list_filters = ("iids", "state", "search")
class GroupNotificationSettings(NotificationSettings):
@@ -836,9 +974,9 @@ class GroupNotificationSettings(NotificationSettings):
class GroupNotificationSettingsManager(NotificationSettingsManager):
- _path = '/groups/%(group_id)s/notification_settings'
+ _path = "/groups/%(group_id)s/notification_settings"
_obj_cls = GroupNotificationSettings
- _from_parent_attrs = {'group_id': 'id'}
+ _from_parent_attrs = {"group_id": "id"}
class GroupProject(RESTObject):
@@ -846,12 +984,21 @@ class GroupProject(RESTObject):
class GroupProjectManager(ListMixin, RESTManager):
- _path = '/groups/%(group_id)s/projects'
+ _path = "/groups/%(group_id)s/projects"
_obj_cls = GroupProject
- _from_parent_attrs = {'group_id': 'id'}
- _list_filters = ('archived', 'visibility', 'order_by', 'sort', 'search',
- 'ci_enabled_first', 'simple', 'owned', 'starred',
- 'with_custom_attributes')
+ _from_parent_attrs = {"group_id": "id"}
+ _list_filters = (
+ "archived",
+ "visibility",
+ "order_by",
+ "sort",
+ "search",
+ "ci_enabled_first",
+ "simple",
+ "owned",
+ "starred",
+ "with_custom_attributes",
+ )
class GroupSubgroup(RESTObject):
@@ -859,44 +1006,52 @@ class GroupSubgroup(RESTObject):
class GroupSubgroupManager(ListMixin, RESTManager):
- _path = '/groups/%(group_id)s/subgroups'
+ _path = "/groups/%(group_id)s/subgroups"
_obj_cls = GroupSubgroup
- _from_parent_attrs = {'group_id': 'id'}
- _list_filters = ('skip_groups', 'all_available', 'search', 'order_by',
- 'sort', 'statistics', 'owned', 'with_custom_attributes')
+ _from_parent_attrs = {"group_id": "id"}
+ _list_filters = (
+ "skip_groups",
+ "all_available",
+ "search",
+ "order_by",
+ "sort",
+ "statistics",
+ "owned",
+ "with_custom_attributes",
+ )
class GroupVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = 'key'
+ _id_attr = "key"
class GroupVariableManager(CRUDMixin, RESTManager):
- _path = '/groups/%(group_id)s/variables'
+ _path = "/groups/%(group_id)s/variables"
_obj_cls = GroupVariable
- _from_parent_attrs = {'group_id': 'id'}
- _create_attrs = (('key', 'value'), ('protected',))
- _update_attrs = (('key', 'value'), ('protected',))
+ _from_parent_attrs = {"group_id": "id"}
+ _create_attrs = (("key", "value"), ("protected",))
+ _update_attrs = (("key", "value"), ("protected",))
class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'name'
+ _short_print_attr = "name"
_managers = (
- ('accessrequests', 'GroupAccessRequestManager'),
- ('badges', 'GroupBadgeManager'),
- ('boards', 'GroupBoardManager'),
- ('customattributes', 'GroupCustomAttributeManager'),
- ('epics', 'GroupEpicManager'),
- ('issues', 'GroupIssueManager'),
- ('members', 'GroupMemberManager'),
- ('mergerequests', 'GroupMergeRequestManager'),
- ('milestones', 'GroupMilestoneManager'),
- ('notificationsettings', 'GroupNotificationSettingsManager'),
- ('projects', 'GroupProjectManager'),
- ('subgroups', 'GroupSubgroupManager'),
- ('variables', 'GroupVariableManager'),
+ ("accessrequests", "GroupAccessRequestManager"),
+ ("badges", "GroupBadgeManager"),
+ ("boards", "GroupBoardManager"),
+ ("customattributes", "GroupCustomAttributeManager"),
+ ("epics", "GroupEpicManager"),
+ ("issues", "GroupIssueManager"),
+ ("members", "GroupMemberManager"),
+ ("mergerequests", "GroupMergeRequestManager"),
+ ("milestones", "GroupMilestoneManager"),
+ ("notificationsettings", "GroupNotificationSettingsManager"),
+ ("projects", "GroupProjectManager"),
+ ("subgroups", "GroupSubgroupManager"),
+ ("variables", "GroupVariableManager"),
)
- @cli.register_custom_action('Group', ('to_project_id', ))
+ @cli.register_custom_action("Group", ("to_project_id",))
@exc.on_http_error(exc.GitlabTransferProjectError)
def transfer_project(self, to_project_id, **kwargs):
"""Transfer a project to this group.
@@ -909,10 +1064,10 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabTransferProjectError: If the project could not be transfered
"""
- path = '/groups/%s/projects/%s' % (self.id, to_project_id)
+ path = "/groups/%s/projects/%s" % (self.id, to_project_id)
self.manager.gitlab.http_post(path, **kwargs)
- @cli.register_custom_action('Group', ('scope', 'search'))
+ @cli.register_custom_action("Group", ("scope", "search"))
@exc.on_http_error(exc.GitlabSearchError)
def search(self, scope, search, **kwargs):
"""Search the group resources matching the provided string.'
@@ -929,11 +1084,11 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
GitlabList: A list of dicts describing the resources found.
"""
- data = {'scope': scope, 'search': search}
- path = '/groups/%s/search' % self.get_id()
+ data = {"scope": scope, "search": search}
+ path = "/groups/%s/search" % self.get_id()
return self.manager.gitlab.http_list(path, query_data=data, **kwargs)
- @cli.register_custom_action('Group', ('cn', 'group_access', 'provider'))
+ @cli.register_custom_action("Group", ("cn", "group_access", "provider"))
@exc.on_http_error(exc.GitlabCreateError)
def add_ldap_group_link(self, cn, group_access, provider, **kwargs):
"""Add an LDAP group link.
@@ -949,11 +1104,11 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the server cannot perform the request
"""
- path = '/groups/%s/ldap_group_links' % self.get_id()
- data = {'cn': cn, 'group_access': group_access, 'provider': provider}
+ path = "/groups/%s/ldap_group_links" % self.get_id()
+ data = {"cn": cn, "group_access": group_access, "provider": provider}
self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- @cli.register_custom_action('Group', ('cn',), ('provider',))
+ @cli.register_custom_action("Group", ("cn",), ("provider",))
@exc.on_http_error(exc.GitlabDeleteError)
def delete_ldap_group_link(self, cn, provider=None, **kwargs):
"""Delete an LDAP group link.
@@ -967,13 +1122,13 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server cannot perform the request
"""
- path = '/groups/%s/ldap_group_links' % self.get_id()
+ path = "/groups/%s/ldap_group_links" % self.get_id()
if provider is not None:
- path += '/%s' % provider
- path += '/%s' % cn
+ path += "/%s" % provider
+ path += "/%s" % cn
self.manager.gitlab.http_delete(path)
- @cli.register_custom_action('Group')
+ @cli.register_custom_action("Group")
@exc.on_http_error(exc.GitlabCreateError)
def ldap_sync(self, **kwargs):
"""Sync LDAP groups.
@@ -985,51 +1140,83 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the server cannot perform the request
"""
- path = '/groups/%s/ldap_sync' % self.get_id()
+ path = "/groups/%s/ldap_sync" % self.get_id()
self.manager.gitlab.http_post(path, **kwargs)
class GroupManager(CRUDMixin, RESTManager):
- _path = '/groups'
+ _path = "/groups"
_obj_cls = Group
- _list_filters = ('skip_groups', 'all_available', 'search', 'order_by',
- 'sort', 'statistics', 'owned', 'with_custom_attributes')
+ _list_filters = (
+ "skip_groups",
+ "all_available",
+ "search",
+ "order_by",
+ "sort",
+ "statistics",
+ "owned",
+ "with_custom_attributes",
+ )
_create_attrs = (
- ('name', 'path'),
- ('description', 'visibility', 'parent_id', 'lfs_enabled',
- 'request_access_enabled')
+ ("name", "path"),
+ (
+ "description",
+ "visibility",
+ "parent_id",
+ "lfs_enabled",
+ "request_access_enabled",
+ ),
)
_update_attrs = (
tuple(),
- ('name', 'path', 'description', 'visibility', 'lfs_enabled',
- 'request_access_enabled')
+ (
+ "name",
+ "path",
+ "description",
+ "visibility",
+ "lfs_enabled",
+ "request_access_enabled",
+ ),
)
class Hook(ObjectDeleteMixin, RESTObject):
- _url = '/hooks'
- _short_print_attr = 'url'
+ _url = "/hooks"
+ _short_print_attr = "url"
class HookManager(NoUpdateMixin, RESTManager):
- _path = '/hooks'
+ _path = "/hooks"
_obj_cls = Hook
- _create_attrs = (('url', ), tuple())
+ _create_attrs = (("url",), tuple())
class Issue(RESTObject):
- _url = '/issues'
- _short_print_attr = 'title'
+ _url = "/issues"
+ _short_print_attr = "title"
class IssueManager(ListMixin, RESTManager):
- _path = '/issues'
+ _path = "/issues"
_obj_cls = Issue
- _list_filters = ('state', 'labels', 'milestone', 'scope', 'author_id',
- 'assignee_id', 'my_reaction_emoji', 'iids', 'order_by',
- 'sort', 'search', 'created_after', 'created_before',
- 'updated_after', 'updated_before')
- _types = {'labels': types.ListAttribute}
+ _list_filters = (
+ "state",
+ "labels",
+ "milestone",
+ "scope",
+ "author_id",
+ "assignee_id",
+ "my_reaction_emoji",
+ "iids",
+ "order_by",
+ "sort",
+ "search",
+ "created_after",
+ "created_before",
+ "updated_after",
+ "updated_before",
+ )
+ _types = {"labels": types.ListAttribute}
class LDAPGroup(RESTObject):
@@ -1037,9 +1224,9 @@ class LDAPGroup(RESTObject):
class LDAPGroupManager(RESTManager):
- _path = '/ldap/groups'
+ _path = "/ldap/groups"
_obj_cls = LDAPGroup
- _list_filters = ('search', 'provider')
+ _list_filters = ("search", "provider")
@exc.on_http_error(exc.GitlabListError)
def list(self, **kwargs):
@@ -1062,10 +1249,10 @@ class LDAPGroupManager(RESTManager):
"""
data = kwargs.copy()
if self.gitlab.per_page:
- data.setdefault('per_page', self.gitlab.per_page)
+ data.setdefault("per_page", self.gitlab.per_page)
- if 'provider' in data:
- path = '/ldap/%s/groups' % data['provider']
+ if "provider" in data:
+ path = "/ldap/%s/groups" % data["provider"]
else:
path = self._path
@@ -1077,14 +1264,14 @@ class LDAPGroupManager(RESTManager):
class License(RESTObject):
- _id_attr = 'key'
+ _id_attr = "key"
class LicenseManager(RetrieveMixin, RESTManager):
- _path = '/templates/licenses'
+ _path = "/templates/licenses"
_obj_cls = License
- _list_filters = ('popular', )
- _optional_get_attrs = ('project', 'fullname')
+ _list_filters = ("popular",)
+ _optional_get_attrs = ("project", "fullname")
class MergeRequest(RESTObject):
@@ -1092,21 +1279,35 @@ class MergeRequest(RESTObject):
class MergeRequestManager(ListMixin, RESTManager):
- _path = '/merge_requests'
+ _path = "/merge_requests"
_obj_cls = MergeRequest
- _from_parent_attrs = {'group_id': 'id'}
- _list_filters = ('state', 'order_by', 'sort', 'milestone', 'view',
- 'labels', 'created_after', 'created_before',
- 'updated_after', 'updated_before', 'scope', 'author_id',
- 'assignee_id', 'my_reaction_emoji', 'source_branch',
- 'target_branch', 'search')
- _types = {'labels': types.ListAttribute}
+ _from_parent_attrs = {"group_id": "id"}
+ _list_filters = (
+ "state",
+ "order_by",
+ "sort",
+ "milestone",
+ "view",
+ "labels",
+ "created_after",
+ "created_before",
+ "updated_after",
+ "updated_before",
+ "scope",
+ "author_id",
+ "assignee_id",
+ "my_reaction_emoji",
+ "source_branch",
+ "target_branch",
+ "search",
+ )
+ _types = {"labels": types.ListAttribute}
class Snippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'title'
+ _short_print_attr = "title"
- @cli.register_custom_action('Snippet')
+ @cli.register_custom_action("Snippet")
@exc.on_http_error(exc.GitlabGetError)
def content(self, streamed=False, action=None, chunk_size=1024, **kwargs):
"""Return the content of a snippet.
@@ -1127,21 +1328,20 @@ class Snippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
str: The snippet content
"""
- path = '/snippets/%s/raw' % self.get_id()
- result = self.manager.gitlab.http_get(path, streamed=streamed,
- raw=True, **kwargs)
+ path = "/snippets/%s/raw" % self.get_id()
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
class SnippetManager(CRUDMixin, RESTManager):
- _path = '/snippets'
+ _path = "/snippets"
_obj_cls = Snippet
- _create_attrs = (('title', 'file_name', 'content'),
- ('lifetime', 'visibility'))
- _update_attrs = (tuple(),
- ('title', 'file_name', 'content', 'visibility'))
+ _create_attrs = (("title", "file_name", "content"), ("lifetime", "visibility"))
+ _update_attrs = (tuple(), ("title", "file_name", "content", "visibility"))
- @cli.register_custom_action('SnippetManager')
+ @cli.register_custom_action("SnippetManager")
def public(self, **kwargs):
"""List all the public snippets.
@@ -1155,7 +1355,7 @@ class SnippetManager(CRUDMixin, RESTManager):
Returns:
RESTObjectList: A generator for the snippets list
"""
- return self.list(path='/snippets/public', **kwargs)
+ return self.list(path="/snippets/public", **kwargs)
class Namespace(RESTObject):
@@ -1163,44 +1363,44 @@ class Namespace(RESTObject):
class NamespaceManager(RetrieveMixin, RESTManager):
- _path = '/namespaces'
+ _path = "/namespaces"
_obj_cls = Namespace
- _list_filters = ('search', )
+ _list_filters = ("search",)
class PagesDomain(RESTObject):
- _id_attr = 'domain'
+ _id_attr = "domain"
class PagesDomainManager(ListMixin, RESTManager):
- _path = '/pages/domains'
+ _path = "/pages/domains"
_obj_cls = PagesDomain
class ProjectRegistryRepository(ObjectDeleteMixin, RESTObject):
- _managers = (
- ('tags', 'ProjectRegistryTagManager'),
- )
+ _managers = (("tags", "ProjectRegistryTagManager"),)
class ProjectRegistryRepositoryManager(DeleteMixin, ListMixin, RESTManager):
- _path = '/projects/%(project_id)s/registry/repositories'
+ _path = "/projects/%(project_id)s/registry/repositories"
_obj_cls = ProjectRegistryRepository
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
class ProjectRegistryTag(ObjectDeleteMixin, RESTObject):
- _id_attr = 'name'
+ _id_attr = "name"
class ProjectRegistryTagManager(DeleteMixin, RetrieveMixin, RESTManager):
_obj_cls = ProjectRegistryTag
- _from_parent_attrs = {'project_id': 'project_id', 'repository_id': 'id'}
- _path = '/projects/%(project_id)s/registry/repositories/%(repository_id)s/tags'
+ _from_parent_attrs = {"project_id": "project_id", "repository_id": "id"}
+ _path = "/projects/%(project_id)s/registry/repositories/%(repository_id)s/tags"
- @cli.register_custom_action('ProjectRegistryTagManager', optional=('name_regex', 'keep_n', 'older_than'))
+ @cli.register_custom_action(
+ "ProjectRegistryTagManager", optional=("name_regex", "keep_n", "older_than")
+ )
@exc.on_http_error(exc.GitlabDeleteError)
- def delete_in_bulk(self, name_regex='.*', **kwargs):
+ def delete_in_bulk(self, name_regex=".*", **kwargs):
"""Delete Tag in bulk
Args:
@@ -1214,8 +1414,8 @@ class ProjectRegistryTagManager(DeleteMixin, RetrieveMixin, RESTManager):
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server cannot perform the request
"""
- valid_attrs = ['keep_n', 'older_than']
- data = {'name_regex': name_regex}
+ valid_attrs = ["keep_n", "older_than"]
+ data = {"name_regex": name_regex}
data.update({k: v for k, v in kwargs.items() if k in valid_attrs})
self.gitlab.http_delete(self.path, query_data=data, **kwargs)
@@ -1225,34 +1425,32 @@ class ProjectBoardList(SaveMixin, ObjectDeleteMixin, RESTObject):
class ProjectBoardListManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/boards/%(board_id)s/lists'
+ _path = "/projects/%(project_id)s/boards/%(board_id)s/lists"
_obj_cls = ProjectBoardList
- _from_parent_attrs = {'project_id': 'project_id',
- 'board_id': 'id'}
- _create_attrs = (('label_id', ), tuple())
- _update_attrs = (('position', ), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "board_id": "id"}
+ _create_attrs = (("label_id",), tuple())
+ _update_attrs = (("position",), tuple())
class ProjectBoard(ObjectDeleteMixin, RESTObject):
- _managers = (('lists', 'ProjectBoardListManager'), )
+ _managers = (("lists", "ProjectBoardListManager"),)
class ProjectBoardManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/boards'
+ _path = "/projects/%(project_id)s/boards"
_obj_cls = ProjectBoard
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('name', ), tuple())
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("name",), tuple())
class ProjectBranch(ObjectDeleteMixin, RESTObject):
- _id_attr = 'name'
+ _id_attr = "name"
- @cli.register_custom_action('ProjectBranch', tuple(),
- ('developers_can_push',
- 'developers_can_merge'))
+ @cli.register_custom_action(
+ "ProjectBranch", tuple(), ("developers_can_push", "developers_can_merge")
+ )
@exc.on_http_error(exc.GitlabProtectError)
- def protect(self, developers_can_push=False, developers_can_merge=False,
- **kwargs):
+ def protect(self, developers_can_push=False, developers_can_merge=False, **kwargs):
"""Protect the branch.
Args:
@@ -1266,14 +1464,16 @@ class ProjectBranch(ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabProtectError: If the branch could not be protected
"""
- id = self.get_id().replace('/', '%2F')
- path = '%s/%s/protect' % (self.manager.path, id)
- post_data = {'developers_can_push': developers_can_push,
- 'developers_can_merge': developers_can_merge}
+ id = self.get_id().replace("/", "%2F")
+ path = "%s/%s/protect" % (self.manager.path, id)
+ post_data = {
+ "developers_can_push": developers_can_push,
+ "developers_can_merge": developers_can_merge,
+ }
self.manager.gitlab.http_put(path, post_data=post_data, **kwargs)
- self._attrs['protected'] = True
+ self._attrs["protected"] = True
- @cli.register_custom_action('ProjectBranch')
+ @cli.register_custom_action("ProjectBranch")
@exc.on_http_error(exc.GitlabProtectError)
def unprotect(self, **kwargs):
"""Unprotect the branch.
@@ -1285,32 +1485,31 @@ class ProjectBranch(ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabProtectError: If the branch could not be unprotected
"""
- id = self.get_id().replace('/', '%2F')
- path = '%s/%s/unprotect' % (self.manager.path, id)
+ id = self.get_id().replace("/", "%2F")
+ path = "%s/%s/unprotect" % (self.manager.path, id)
self.manager.gitlab.http_put(path, **kwargs)
- self._attrs['protected'] = False
+ self._attrs["protected"] = False
class ProjectBranchManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/repository/branches'
+ _path = "/projects/%(project_id)s/repository/branches"
_obj_cls = ProjectBranch
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('branch', 'ref'), tuple())
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("branch", "ref"), tuple())
class ProjectCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = 'key'
+ _id_attr = "key"
-class ProjectCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin,
- RESTManager):
- _path = '/projects/%(project_id)s/custom_attributes'
+class ProjectCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
+ _path = "/projects/%(project_id)s/custom_attributes"
_obj_cls = ProjectCustomAttribute
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
class ProjectJob(RESTObject, RefreshMixin):
- @cli.register_custom_action('ProjectJob')
+ @cli.register_custom_action("ProjectJob")
@exc.on_http_error(exc.GitlabJobCancelError)
def cancel(self, **kwargs):
"""Cancel the job.
@@ -1322,10 +1521,10 @@ class ProjectJob(RESTObject, RefreshMixin):
GitlabAuthenticationError: If authentication is not correct
GitlabJobCancelError: If the job could not be canceled
"""
- path = '%s/%s/cancel' % (self.manager.path, self.get_id())
+ path = "%s/%s/cancel" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path)
- @cli.register_custom_action('ProjectJob')
+ @cli.register_custom_action("ProjectJob")
@exc.on_http_error(exc.GitlabJobRetryError)
def retry(self, **kwargs):
"""Retry the job.
@@ -1337,10 +1536,10 @@ class ProjectJob(RESTObject, RefreshMixin):
GitlabAuthenticationError: If authentication is not correct
GitlabJobRetryError: If the job could not be retried
"""
- path = '%s/%s/retry' % (self.manager.path, self.get_id())
+ path = "%s/%s/retry" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path)
- @cli.register_custom_action('ProjectJob')
+ @cli.register_custom_action("ProjectJob")
@exc.on_http_error(exc.GitlabJobPlayError)
def play(self, **kwargs):
"""Trigger a job explicitly.
@@ -1352,10 +1551,10 @@ class ProjectJob(RESTObject, RefreshMixin):
GitlabAuthenticationError: If authentication is not correct
GitlabJobPlayError: If the job could not be triggered
"""
- path = '%s/%s/play' % (self.manager.path, self.get_id())
+ path = "%s/%s/play" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path)
- @cli.register_custom_action('ProjectJob')
+ @cli.register_custom_action("ProjectJob")
@exc.on_http_error(exc.GitlabJobEraseError)
def erase(self, **kwargs):
"""Erase the job (remove job artifacts and trace).
@@ -1367,10 +1566,10 @@ class ProjectJob(RESTObject, RefreshMixin):
GitlabAuthenticationError: If authentication is not correct
GitlabJobEraseError: If the job could not be erased
"""
- path = '%s/%s/erase' % (self.manager.path, self.get_id())
+ path = "%s/%s/erase" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path)
- @cli.register_custom_action('ProjectJob')
+ @cli.register_custom_action("ProjectJob")
@exc.on_http_error(exc.GitlabCreateError)
def keep_artifacts(self, **kwargs):
"""Prevent artifacts from being deleted when expiration is set.
@@ -1382,13 +1581,12 @@ class ProjectJob(RESTObject, RefreshMixin):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the request could not be performed
"""
- path = '%s/%s/artifacts/keep' % (self.manager.path, self.get_id())
+ path = "%s/%s/artifacts/keep" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path)
- @cli.register_custom_action('ProjectJob')
+ @cli.register_custom_action("ProjectJob")
@exc.on_http_error(exc.GitlabGetError)
- def artifacts(self, streamed=False, action=None, chunk_size=1024,
- **kwargs):
+ def artifacts(self, streamed=False, action=None, chunk_size=1024, **kwargs):
"""Get the job artifacts.
Args:
@@ -1407,15 +1605,15 @@ class ProjectJob(RESTObject, RefreshMixin):
Returns:
str: The artifacts if `streamed` is False, None otherwise.
"""
- path = '%s/%s/artifacts' % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, streamed=streamed,
- raw=True, **kwargs)
+ path = "%s/%s/artifacts" % (self.manager.path, self.get_id())
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
- @cli.register_custom_action('ProjectJob')
+ @cli.register_custom_action("ProjectJob")
@exc.on_http_error(exc.GitlabGetError)
- def artifact(self, path, streamed=False, action=None, chunk_size=1024,
- **kwargs):
+ def artifact(self, path, streamed=False, action=None, chunk_size=1024, **kwargs):
"""Get a single artifact file from within the job's artifacts archive.
Args:
@@ -1435,12 +1633,13 @@ class ProjectJob(RESTObject, RefreshMixin):
Returns:
str: The artifacts if `streamed` is False, None otherwise.
"""
- path = '%s/%s/artifacts/%s' % (self.manager.path, self.get_id(), path)
- result = self.manager.gitlab.http_get(path, streamed=streamed,
- raw=True, **kwargs)
+ path = "%s/%s/artifacts/%s" % (self.manager.path, self.get_id(), path)
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
- @cli.register_custom_action('ProjectJob')
+ @cli.register_custom_action("ProjectJob")
@exc.on_http_error(exc.GitlabGetError)
def trace(self, streamed=False, action=None, chunk_size=1024, **kwargs):
"""Get the job trace.
@@ -1461,16 +1660,17 @@ class ProjectJob(RESTObject, RefreshMixin):
Returns:
str: The trace
"""
- path = '%s/%s/trace' % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, streamed=streamed,
- raw=True, **kwargs)
+ path = "%s/%s/trace" % (self.manager.path, self.get_id())
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
class ProjectJobManager(RetrieveMixin, RESTManager):
- _path = '/projects/%(project_id)s/jobs'
+ _path = "/projects/%(project_id)s/jobs"
_obj_cls = ProjectJob
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
class ProjectCommitStatus(RESTObject, RefreshMixin):
@@ -1478,13 +1678,13 @@ class ProjectCommitStatus(RESTObject, RefreshMixin):
class ProjectCommitStatusManager(ListMixin, CreateMixin, RESTManager):
- _path = ('/projects/%(project_id)s/repository/commits/%(commit_id)s'
- '/statuses')
+ _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s" "/statuses"
_obj_cls = ProjectCommitStatus
- _from_parent_attrs = {'project_id': 'project_id', 'commit_id': 'id'}
- _create_attrs = (('state', ),
- ('description', 'name', 'context', 'ref', 'target_url',
- 'coverage'))
+ _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
+ _create_attrs = (
+ ("state",),
+ ("description", "name", "context", "ref", "target_url", "coverage"),
+ )
@exc.on_http_error(exc.GitlabCreateError)
def create(self, data, **kwargs):
@@ -1507,8 +1707,8 @@ class ProjectCommitStatusManager(ListMixin, CreateMixin, RESTManager):
# project_id and commit_id are in the data dict when using the CLI, but
# they are missing when using only the API
# See #511
- base_path = '/projects/%(project_id)s/statuses/%(commit_id)s'
- if 'project_id' in data and 'commit_id' in data:
+ base_path = "/projects/%(project_id)s/statuses/%(commit_id)s"
+ if "project_id" in data and "commit_id" in data:
path = base_path % data
else:
path = self._compute_path(base_path)
@@ -1517,54 +1717,57 @@ class ProjectCommitStatusManager(ListMixin, CreateMixin, RESTManager):
class ProjectCommitComment(RESTObject):
_id_attr = None
- _short_print_attr = 'note'
+ _short_print_attr = "note"
class ProjectCommitCommentManager(ListMixin, CreateMixin, RESTManager):
- _path = ('/projects/%(project_id)s/repository/commits/%(commit_id)s'
- '/comments')
+ _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s" "/comments"
_obj_cls = ProjectCommitComment
- _from_parent_attrs = {'project_id': 'project_id', 'commit_id': 'id'}
- _create_attrs = (('note', ), ('path', 'line', 'line_type'))
+ _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
+ _create_attrs = (("note",), ("path", "line", "line_type"))
class ProjectCommitDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
-class ProjectCommitDiscussionNoteManager(GetMixin, CreateMixin, UpdateMixin,
- DeleteMixin, RESTManager):
- _path = ('/projects/%(project_id)s/repository/commits/%(commit_id)s/'
- 'discussions/%(discussion_id)s/notes')
+class ProjectCommitDiscussionNoteManager(
+ GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = (
+ "/projects/%(project_id)s/repository/commits/%(commit_id)s/"
+ "discussions/%(discussion_id)s/notes"
+ )
_obj_cls = ProjectCommitDiscussionNote
- _from_parent_attrs = {'project_id': 'project_id',
- 'commit_id': 'commit_id',
- 'discussion_id': 'id'}
- _create_attrs = (('body',), ('created_at', 'position'))
- _update_attrs = (('body',), tuple())
+ _from_parent_attrs = {
+ "project_id": "project_id",
+ "commit_id": "commit_id",
+ "discussion_id": "id",
+ }
+ _create_attrs = (("body",), ("created_at", "position"))
+ _update_attrs = (("body",), tuple())
class ProjectCommitDiscussion(RESTObject):
- _managers = (('notes', 'ProjectCommitDiscussionNoteManager'),)
+ _managers = (("notes", "ProjectCommitDiscussionNoteManager"),)
class ProjectCommitDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = ('/projects/%(project_id)s/repository/commits/%(commit_id)s/'
- 'discussions')
+ _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s/" "discussions"
_obj_cls = ProjectCommitDiscussion
- _from_parent_attrs = {'project_id': 'project_id', 'commit_id': 'id'}
- _create_attrs = (('body',), ('created_at',))
+ _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
+ _create_attrs = (("body",), ("created_at",))
class ProjectCommit(RESTObject):
- _short_print_attr = 'title'
+ _short_print_attr = "title"
_managers = (
- ('comments', 'ProjectCommitCommentManager'),
- ('discussions', 'ProjectCommitDiscussionManager'),
- ('statuses', 'ProjectCommitStatusManager'),
+ ("comments", "ProjectCommitCommentManager"),
+ ("discussions", "ProjectCommitDiscussionManager"),
+ ("statuses", "ProjectCommitStatusManager"),
)
- @cli.register_custom_action('ProjectCommit')
+ @cli.register_custom_action("ProjectCommit")
@exc.on_http_error(exc.GitlabGetError)
def diff(self, **kwargs):
"""Generate the commit diff.
@@ -1579,10 +1782,10 @@ class ProjectCommit(RESTObject):
Returns:
list: The changes done in this commit
"""
- path = '%s/%s/diff' % (self.manager.path, self.get_id())
+ path = "%s/%s/diff" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)
- @cli.register_custom_action('ProjectCommit', ('branch',))
+ @cli.register_custom_action("ProjectCommit", ("branch",))
@exc.on_http_error(exc.GitlabCherryPickError)
def cherry_pick(self, branch, **kwargs):
"""Cherry-pick a commit into a branch.
@@ -1595,13 +1798,13 @@ class ProjectCommit(RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCherryPickError: If the cherry-pick could not be performed
"""
- path = '%s/%s/cherry_pick' % (self.manager.path, self.get_id())
- post_data = {'branch': branch}
+ path = "%s/%s/cherry_pick" % (self.manager.path, self.get_id())
+ post_data = {"branch": branch}
self.manager.gitlab.http_post(path, post_data=post_data, **kwargs)
- @cli.register_custom_action('ProjectCommit', optional=('type',))
+ @cli.register_custom_action("ProjectCommit", optional=("type",))
@exc.on_http_error(exc.GitlabGetError)
- def refs(self, type='all', **kwargs):
+ def refs(self, type="all", **kwargs):
"""List the references the commit is pushed to.
Args:
@@ -1615,11 +1818,11 @@ class ProjectCommit(RESTObject):
Returns:
list: The references the commit is pushed to.
"""
- path = '%s/%s/refs' % (self.manager.path, self.get_id())
- data = {'type': type}
+ path = "%s/%s/refs" % (self.manager.path, self.get_id())
+ data = {"type": type}
return self.manager.gitlab.http_get(path, query_data=data, **kwargs)
- @cli.register_custom_action('ProjectCommit')
+ @cli.register_custom_action("ProjectCommit")
@exc.on_http_error(exc.GitlabGetError)
def merge_requests(self, **kwargs):
"""List the merge requests related to the commit.
@@ -1634,20 +1837,22 @@ class ProjectCommit(RESTObject):
Returns:
list: The merge requests related to the commit.
"""
- path = '%s/%s/merge_requests' % (self.manager.path, self.get_id())
+ path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)
class ProjectCommitManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = '/projects/%(project_id)s/repository/commits'
+ _path = "/projects/%(project_id)s/repository/commits"
_obj_cls = ProjectCommit
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('branch', 'commit_message', 'actions'),
- ('author_email', 'author_name'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (
+ ("branch", "commit_message", "actions"),
+ ("author_email", "author_name"),
+ )
class ProjectEnvironment(SaveMixin, ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action('ProjectEnvironment')
+ @cli.register_custom_action("ProjectEnvironment")
@exc.on_http_error(exc.GitlabStopError)
def stop(self, **kwargs):
"""Stop the environment.
@@ -1659,17 +1864,18 @@ class ProjectEnvironment(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabStopError: If the operation failed
"""
- path = '%s/%s/stop' % (self.manager.path, self.get_id())
+ path = "%s/%s/stop" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path, **kwargs)
-class ProjectEnvironmentManager(ListMixin, CreateMixin, UpdateMixin,
- DeleteMixin, RESTManager):
- _path = '/projects/%(project_id)s/environments'
+class ProjectEnvironmentManager(
+ ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = "/projects/%(project_id)s/environments"
_obj_cls = ProjectEnvironment
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('name', ), ('external_url', ))
- _update_attrs = (tuple(), ('name', 'external_url'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("name",), ("external_url",))
+ _update_attrs = (tuple(), ("name", "external_url"))
class ProjectKey(SaveMixin, ObjectDeleteMixin, RESTObject):
@@ -1677,13 +1883,13 @@ class ProjectKey(SaveMixin, ObjectDeleteMixin, RESTObject):
class ProjectKeyManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/deploy_keys'
+ _path = "/projects/%(project_id)s/deploy_keys"
_obj_cls = ProjectKey
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('title', 'key'), ('can_push',))
- _update_attrs = (tuple(), ('title', 'can_push'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("title", "key"), ("can_push",))
+ _update_attrs = (tuple(), ("title", "can_push"))
- @cli.register_custom_action('ProjectKeyManager', ('key_id',))
+ @cli.register_custom_action("ProjectKeyManager", ("key_id",))
@exc.on_http_error(exc.GitlabProjectDeployKeyError)
def enable(self, key_id, **kwargs):
"""Enable a deploy key for a project.
@@ -1696,7 +1902,7 @@ class ProjectKeyManager(CRUDMixin, RESTManager):
GitlabAuthenticationError: If authentication is not correct
GitlabProjectDeployKeyError: If the key could not be enabled
"""
- path = '%s/%s/enable' % (self.path, key_id)
+ path = "%s/%s/enable" % (self.path, key_id)
self.gitlab.http_post(path, **kwargs)
@@ -1705,11 +1911,11 @@ class ProjectBadge(SaveMixin, ObjectDeleteMixin, RESTObject):
class ProjectBadgeManager(BadgeRenderMixin, CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/badges'
+ _path = "/projects/%(project_id)s/badges"
_obj_cls = ProjectBadge
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('link_url', 'image_url'), tuple())
- _update_attrs = (tuple(), ('link_url', 'image_url'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("link_url", "image_url"), tuple())
+ _update_attrs = (tuple(), ("link_url", "image_url"))
class ProjectEvent(Event):
@@ -1717,9 +1923,9 @@ class ProjectEvent(Event):
class ProjectEventManager(EventManager):
- _path = '/projects/%(project_id)s/events'
+ _path = "/projects/%(project_id)s/events"
_obj_cls = ProjectEvent
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
class ProjectFork(RESTObject):
@@ -1727,14 +1933,25 @@ class ProjectFork(RESTObject):
class ProjectForkManager(CreateMixin, ListMixin, RESTManager):
- _path = '/projects/%(project_id)s/fork'
+ _path = "/projects/%(project_id)s/fork"
_obj_cls = ProjectFork
- _from_parent_attrs = {'project_id': 'id'}
- _list_filters = ('archived', 'visibility', 'order_by', 'sort', 'search',
- 'simple', 'owned', 'membership', 'starred', 'statistics',
- 'with_custom_attributes', 'with_issues_enabled',
- 'with_merge_requests_enabled')
- _create_attrs = (tuple(), ('namespace', ))
+ _from_parent_attrs = {"project_id": "id"}
+ _list_filters = (
+ "archived",
+ "visibility",
+ "order_by",
+ "sort",
+ "search",
+ "simple",
+ "owned",
+ "membership",
+ "starred",
+ "statistics",
+ "with_custom_attributes",
+ "with_issues_enabled",
+ "with_merge_requests_enabled",
+ )
+ _create_attrs = (tuple(), ("namespace",))
def list(self, **kwargs):
"""Retrieve a list of objects.
@@ -1755,31 +1972,49 @@ class ProjectForkManager(CreateMixin, ListMixin, RESTManager):
GitlabListError: If the server cannot perform the request
"""
- path = self._compute_path('/projects/%(project_id)s/forks')
+ path = self._compute_path("/projects/%(project_id)s/forks")
return ListMixin.list(self, path=path, **kwargs)
class ProjectHook(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'url'
+ _short_print_attr = "url"
class ProjectHookManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/hooks'
+ _path = "/projects/%(project_id)s/hooks"
_obj_cls = ProjectHook
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
_create_attrs = (
- ('url', ),
- ('push_events', 'issues_events', 'confidential_issues_events',
- 'merge_requests_events', 'tag_push_events', 'note_events',
- 'job_events', 'pipeline_events', 'wiki_page_events',
- 'enable_ssl_verification', 'token')
+ ("url",),
+ (
+ "push_events",
+ "issues_events",
+ "confidential_issues_events",
+ "merge_requests_events",
+ "tag_push_events",
+ "note_events",
+ "job_events",
+ "pipeline_events",
+ "wiki_page_events",
+ "enable_ssl_verification",
+ "token",
+ ),
)
_update_attrs = (
- ('url', ),
- ('push_events', 'issues_events', 'confidential_issues_events',
- 'merge_requests_events', 'tag_push_events', 'note_events',
- 'job_events', 'pipeline_events', 'wiki_events',
- 'enable_ssl_verification', 'token')
+ ("url",),
+ (
+ "push_events",
+ "issues_events",
+ "confidential_issues_events",
+ "merge_requests_events",
+ "tag_push_events",
+ "note_events",
+ "job_events",
+ "pipeline_events",
+ "wiki_events",
+ "enable_ssl_verification",
+ "token",
+ ),
)
@@ -1788,10 +2023,10 @@ class ProjectIssueAwardEmoji(ObjectDeleteMixin, RESTObject):
class ProjectIssueAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/issues/%(issue_iid)s/award_emoji'
+ _path = "/projects/%(project_id)s/issues/%(issue_iid)s/award_emoji"
_obj_cls = ProjectIssueAwardEmoji
- _from_parent_attrs = {'project_id': 'project_id', 'issue_iid': 'iid'}
- _create_attrs = (('name', ), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
+ _create_attrs = (("name",), tuple())
class ProjectIssueNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
@@ -1799,64 +2034,71 @@ class ProjectIssueNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
class ProjectIssueNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = ('/projects/%(project_id)s/issues/%(issue_iid)s'
- '/notes/%(note_id)s/award_emoji')
+ _path = (
+ "/projects/%(project_id)s/issues/%(issue_iid)s" "/notes/%(note_id)s/award_emoji"
+ )
_obj_cls = ProjectIssueNoteAwardEmoji
- _from_parent_attrs = {'project_id': 'project_id',
- 'issue_iid': 'issue_iid',
- 'note_id': 'id'}
- _create_attrs = (('name', ), tuple())
+ _from_parent_attrs = {
+ "project_id": "project_id",
+ "issue_iid": "issue_iid",
+ "note_id": "id",
+ }
+ _create_attrs = (("name",), tuple())
class ProjectIssueNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- _managers = (('awardemojis', 'ProjectIssueNoteAwardEmojiManager'),)
+ _managers = (("awardemojis", "ProjectIssueNoteAwardEmojiManager"),)
class ProjectIssueNoteManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/issues/%(issue_iid)s/notes'
+ _path = "/projects/%(project_id)s/issues/%(issue_iid)s/notes"
_obj_cls = ProjectIssueNote
- _from_parent_attrs = {'project_id': 'project_id', 'issue_iid': 'iid'}
- _create_attrs = (('body', ), ('created_at', ))
- _update_attrs = (('body', ), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
+ _create_attrs = (("body",), ("created_at",))
+ _update_attrs = (("body",), tuple())
class ProjectIssueDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
-class ProjectIssueDiscussionNoteManager(GetMixin, CreateMixin, UpdateMixin,
- DeleteMixin, RESTManager):
- _path = ('/projects/%(project_id)s/issues/%(issue_iid)s/'
- 'discussions/%(discussion_id)s/notes')
+class ProjectIssueDiscussionNoteManager(
+ GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = (
+ "/projects/%(project_id)s/issues/%(issue_iid)s/"
+ "discussions/%(discussion_id)s/notes"
+ )
_obj_cls = ProjectIssueDiscussionNote
- _from_parent_attrs = {'project_id': 'project_id',
- 'issue_iid': 'issue_iid',
- 'discussion_id': 'id'}
- _create_attrs = (('body',), ('created_at',))
- _update_attrs = (('body',), tuple())
+ _from_parent_attrs = {
+ "project_id": "project_id",
+ "issue_iid": "issue_iid",
+ "discussion_id": "id",
+ }
+ _create_attrs = (("body",), ("created_at",))
+ _update_attrs = (("body",), tuple())
class ProjectIssueDiscussion(RESTObject):
- _managers = (('notes', 'ProjectIssueDiscussionNoteManager'),)
+ _managers = (("notes", "ProjectIssueDiscussionNoteManager"),)
class ProjectIssueDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = '/projects/%(project_id)s/issues/%(issue_iid)s/discussions'
+ _path = "/projects/%(project_id)s/issues/%(issue_iid)s/discussions"
_obj_cls = ProjectIssueDiscussion
- _from_parent_attrs = {'project_id': 'project_id', 'issue_iid': 'iid'}
- _create_attrs = (('body',), ('created_at',))
+ _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
+ _create_attrs = (("body",), ("created_at",))
class ProjectIssueLink(ObjectDeleteMixin, RESTObject):
- _id_attr = 'issue_link_id'
+ _id_attr = "issue_link_id"
-class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin,
- RESTManager):
- _path = '/projects/%(project_id)s/issues/%(issue_iid)s/links'
+class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
+ _path = "/projects/%(project_id)s/issues/%(issue_iid)s/links"
_obj_cls = ProjectIssueLink
- _from_parent_attrs = {'project_id': 'project_id', 'issue_iid': 'iid'}
- _create_attrs = (('target_project_id', 'target_issue_iid'), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
+ _create_attrs = (("target_project_id", "target_issue_iid"), tuple())
@exc.on_http_error(exc.GitlabCreateError)
def create(self, data, **kwargs):
@@ -1875,12 +2117,9 @@ class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin,
GitlabCreateError: If the server cannot perform the request
"""
self._check_missing_create_attrs(data)
- server_data = self.gitlab.http_post(self.path, post_data=data,
- **kwargs)
- source_issue = ProjectIssue(self._parent.manager,
- server_data['source_issue'])
- target_issue = ProjectIssue(self._parent.manager,
- server_data['target_issue'])
+ server_data = self.gitlab.http_post(self.path, post_data=data, **kwargs)
+ source_issue = ProjectIssue(self._parent.manager, server_data["source_issue"])
+ target_issue = ProjectIssue(self._parent.manager, server_data["target_issue"])
return source_issue, target_issue
@@ -1889,26 +2128,32 @@ class ProjectIssueResourceLabelEvent(RESTObject):
class ProjectIssueResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = ('/projects/%(project_id)s/issues/%(issue_iid)s'
- '/resource_label_events')
+ _path = "/projects/%(project_id)s/issues/%(issue_iid)s" "/resource_label_events"
_obj_cls = ProjectIssueResourceLabelEvent
- _from_parent_attrs = {'project_id': 'project_id', 'issue_iid': 'iid'}
-
-
-class ProjectIssue(UserAgentDetailMixin, SubscribableMixin, TodoMixin,
- TimeTrackingMixin, ParticipantsMixin, SaveMixin,
- ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'title'
- _id_attr = 'iid'
+ _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
+
+
+class ProjectIssue(
+ UserAgentDetailMixin,
+ SubscribableMixin,
+ TodoMixin,
+ TimeTrackingMixin,
+ ParticipantsMixin,
+ SaveMixin,
+ ObjectDeleteMixin,
+ RESTObject,
+):
+ _short_print_attr = "title"
+ _id_attr = "iid"
_managers = (
- ('awardemojis', 'ProjectIssueAwardEmojiManager'),
- ('discussions', 'ProjectIssueDiscussionManager'),
- ('links', 'ProjectIssueLinkManager'),
- ('notes', 'ProjectIssueNoteManager'),
- ('resourcelabelevents', 'ProjectIssueResourceLabelEventManager'),
+ ("awardemojis", "ProjectIssueAwardEmojiManager"),
+ ("discussions", "ProjectIssueDiscussionManager"),
+ ("links", "ProjectIssueLinkManager"),
+ ("notes", "ProjectIssueNoteManager"),
+ ("resourcelabelevents", "ProjectIssueResourceLabelEventManager"),
)
- @cli.register_custom_action('ProjectIssue', ('to_project_id',))
+ @cli.register_custom_action("ProjectIssue", ("to_project_id",))
@exc.on_http_error(exc.GitlabUpdateError)
def move(self, to_project_id, **kwargs):
"""Move the issue to another project.
@@ -1921,13 +2166,12 @@ class ProjectIssue(UserAgentDetailMixin, SubscribableMixin, TodoMixin,
GitlabAuthenticationError: If authentication is not correct
GitlabUpdateError: If the issue could not be moved
"""
- path = '%s/%s/move' % (self.manager.path, self.get_id())
- data = {'to_project_id': to_project_id}
- server_data = self.manager.gitlab.http_post(path, post_data=data,
- **kwargs)
+ path = "%s/%s/move" % (self.manager.path, self.get_id())
+ data = {"to_project_id": to_project_id}
+ server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('ProjectIssue')
+ @cli.register_custom_action("ProjectIssue")
@exc.on_http_error(exc.GitlabGetError)
def closed_by(self, **kwargs):
"""List merge requests that will close the issue when merged.
@@ -1942,42 +2186,77 @@ class ProjectIssue(UserAgentDetailMixin, SubscribableMixin, TodoMixin,
Returns:
list: The list of merge requests.
"""
- path = '%s/%s/closed_by' % (self.manager.path, self.get_id())
+ path = "%s/%s/closed_by" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)
class ProjectIssueManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/issues'
+ _path = "/projects/%(project_id)s/issues"
_obj_cls = ProjectIssue
- _from_parent_attrs = {'project_id': 'id'}
- _list_filters = ('iids', 'state', 'labels', 'milestone', 'scope',
- 'author_id', 'assignee_id', 'my_reaction_emoji',
- 'order_by', 'sort', 'search', 'created_after',
- 'created_before', 'updated_after', 'updated_before')
- _create_attrs = (('title', ),
- ('description', 'confidential', 'assignee_ids',
- 'assignee_id', 'milestone_id', 'labels', 'created_at',
- 'due_date', 'merge_request_to_resolve_discussions_of',
- 'discussion_to_resolve'))
- _update_attrs = (tuple(), ('title', 'description', 'confidential',
- 'assignee_ids', 'assignee_id', 'milestone_id',
- 'labels', 'state_event', 'updated_at',
- 'due_date', 'discussion_locked'))
- _types = {'labels': types.ListAttribute}
+ _from_parent_attrs = {"project_id": "id"}
+ _list_filters = (
+ "iids",
+ "state",
+ "labels",
+ "milestone",
+ "scope",
+ "author_id",
+ "assignee_id",
+ "my_reaction_emoji",
+ "order_by",
+ "sort",
+ "search",
+ "created_after",
+ "created_before",
+ "updated_after",
+ "updated_before",
+ )
+ _create_attrs = (
+ ("title",),
+ (
+ "description",
+ "confidential",
+ "assignee_ids",
+ "assignee_id",
+ "milestone_id",
+ "labels",
+ "created_at",
+ "due_date",
+ "merge_request_to_resolve_discussions_of",
+ "discussion_to_resolve",
+ ),
+ )
+ _update_attrs = (
+ tuple(),
+ (
+ "title",
+ "description",
+ "confidential",
+ "assignee_ids",
+ "assignee_id",
+ "milestone_id",
+ "labels",
+ "state_event",
+ "updated_at",
+ "due_date",
+ "discussion_locked",
+ ),
+ )
+ _types = {"labels": types.ListAttribute}
class ProjectMember(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'username'
+ _short_print_attr = "username"
class ProjectMemberManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/members'
+ _path = "/projects/%(project_id)s/members"
_obj_cls = ProjectMember
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('access_level', 'user_id'), ('expires_at', ))
- _update_attrs = (('access_level', ), ('expires_at', ))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("access_level", "user_id"), ("expires_at",))
+ _update_attrs = (("access_level",), ("expires_at",))
- @cli.register_custom_action('ProjectMemberManager')
+ @cli.register_custom_action("ProjectMemberManager")
@exc.on_http_error(exc.GitlabListError)
def all(self, **kwargs):
"""List all the members, included inherited ones.
@@ -1998,7 +2277,7 @@ class ProjectMemberManager(CRUDMixin, RESTManager):
RESTObjectList: The list of members
"""
- path = '%s/all' % self.path
+ path = "%s/all" % self.path
obj = self.gitlab.http_list(path, **kwargs)
return [self._obj_cls(self, item) for item in obj]
@@ -2008,10 +2287,10 @@ class ProjectNote(RESTObject):
class ProjectNoteManager(RetrieveMixin, RESTManager):
- _path = '/projects/%(project_id)s/notes'
+ _path = "/projects/%(project_id)s/notes"
_obj_cls = ProjectNote
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('body', ), tuple())
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("body",), tuple())
class ProjectNotificationSettings(NotificationSettings):
@@ -2019,40 +2298,39 @@ class ProjectNotificationSettings(NotificationSettings):
class ProjectNotificationSettingsManager(NotificationSettingsManager):
- _path = '/projects/%(project_id)s/notification_settings'
+ _path = "/projects/%(project_id)s/notification_settings"
_obj_cls = ProjectNotificationSettings
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
class ProjectPagesDomain(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = 'domain'
+ _id_attr = "domain"
class ProjectPagesDomainManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/pages/domains'
+ _path = "/projects/%(project_id)s/pages/domains"
_obj_cls = ProjectPagesDomain
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('domain', ), ('certificate', 'key'))
- _update_attrs = (tuple(), ('certificate', 'key'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("domain",), ("certificate", "key"))
+ _update_attrs = (tuple(), ("certificate", "key"))
class ProjectRelease(RESTObject):
- _id_attr = 'tag_name'
+ _id_attr = "tag_name"
class ProjectReleaseManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/releases'
+ _path = "/projects/%(project_id)s/releases"
_obj_cls = ProjectRelease
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('name', 'tag_name', 'description', ),
- ('ref', 'assets', ))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("name", "tag_name", "description"), ("ref", "assets"))
class ProjectTag(ObjectDeleteMixin, RESTObject):
- _id_attr = 'name'
- _short_print_attr = 'name'
+ _id_attr = "name"
+ _short_print_attr = "name"
- @cli.register_custom_action('ProjectTag', ('description', ))
+ @cli.register_custom_action("ProjectTag", ("description",))
def set_release_description(self, description, **kwargs):
"""Set the release notes on the tag.
@@ -2068,55 +2346,54 @@ class ProjectTag(ObjectDeleteMixin, RESTObject):
GitlabCreateError: If the server fails to create the release
GitlabUpdateError: If the server fails to update the release
"""
- id = self.get_id().replace('/', '%2F')
- path = '%s/%s/release' % (self.manager.path, id)
- data = {'description': description}
+ id = self.get_id().replace("/", "%2F")
+ path = "%s/%s/release" % (self.manager.path, id)
+ data = {"description": description}
if self.release is None:
try:
- server_data = self.manager.gitlab.http_post(path,
- post_data=data,
- **kwargs)
+ server_data = self.manager.gitlab.http_post(
+ path, post_data=data, **kwargs
+ )
except exc.GitlabHttpError as e:
raise exc.GitlabCreateError(e.response_code, e.error_message)
else:
try:
- server_data = self.manager.gitlab.http_put(path,
- post_data=data,
- **kwargs)
+ server_data = self.manager.gitlab.http_put(
+ path, post_data=data, **kwargs
+ )
except exc.GitlabHttpError as e:
raise exc.GitlabUpdateError(e.response_code, e.error_message)
self.release = server_data
class ProjectTagManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/repository/tags'
+ _path = "/projects/%(project_id)s/repository/tags"
_obj_cls = ProjectTag
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('tag_name', 'ref'), ('message',))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("tag_name", "ref"), ("message",))
class ProjectProtectedTag(ObjectDeleteMixin, RESTObject):
- _id_attr = 'name'
- _short_print_attr = 'name'
+ _id_attr = "name"
+ _short_print_attr = "name"
class ProjectProtectedTagManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/protected_tags'
+ _path = "/projects/%(project_id)s/protected_tags"
_obj_cls = ProjectProtectedTag
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('name',), ('create_access_level',))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("name",), ("create_access_level",))
class ProjectMergeRequestApproval(SaveMixin, RESTObject):
_id_attr = None
-class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin,
- RESTManager):
- _path = '/projects/%(project_id)s/merge_requests/%(mr_iid)s/approvals'
+class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
+ _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/approvals"
_obj_cls = ProjectMergeRequestApproval
- _from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'}
- _update_attrs = (('approvals_required',), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
+ _update_attrs = (("approvals_required",), tuple())
_update_uses_post = True
@exc.on_http_error(exc.GitlabUpdateError)
@@ -2131,10 +2408,8 @@ class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin,
GitlabAuthenticationError: If authentication is not correct
GitlabUpdateError: If the server failed to perform the request
"""
- path = '%s/%s/approvers' % (self._parent.manager.path,
- self._parent.get_id())
- data = {'approver_ids': approver_ids,
- 'approver_group_ids': approver_group_ids}
+ path = "%s/%s/approvers" % (self._parent.manager.path, self._parent.get_id())
+ data = {"approver_ids": approver_ids, "approver_group_ids": approver_group_ids}
self.gitlab.http_put(path, post_data=data, **kwargs)
@@ -2143,10 +2418,10 @@ class ProjectMergeRequestAwardEmoji(ObjectDeleteMixin, RESTObject):
class ProjectMergeRequestAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/merge_requests/%(mr_iid)s/award_emoji'
+ _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/award_emoji"
_obj_cls = ProjectMergeRequestAwardEmoji
- _from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'}
- _create_attrs = (('name', ), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
+ _create_attrs = (("name",), tuple())
class ProjectMergeRequestDiff(RESTObject):
@@ -2154,9 +2429,9 @@ class ProjectMergeRequestDiff(RESTObject):
class ProjectMergeRequestDiffManager(RetrieveMixin, RESTManager):
- _path = '/projects/%(project_id)s/merge_requests/%(mr_iid)s/versions'
+ _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/versions"
_obj_cls = ProjectMergeRequestDiff
- _from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'}
+ _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
class ProjectMergeRequestNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
@@ -2164,56 +2439,64 @@ class ProjectMergeRequestNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
class ProjectMergeRequestNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = ('/projects/%(project_id)s/merge_requests/%(mr_iid)s'
- '/notes/%(note_id)s/award_emoji')
+ _path = (
+ "/projects/%(project_id)s/merge_requests/%(mr_iid)s"
+ "/notes/%(note_id)s/award_emoji"
+ )
_obj_cls = ProjectMergeRequestNoteAwardEmoji
- _from_parent_attrs = {'project_id': 'project_id',
- 'mr_iid': 'mr_iid',
- 'note_id': 'id'}
- _create_attrs = (('name', ), tuple())
+ _from_parent_attrs = {
+ "project_id": "project_id",
+ "mr_iid": "mr_iid",
+ "note_id": "id",
+ }
+ _create_attrs = (("name",), tuple())
class ProjectMergeRequestNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- _managers = (('awardemojis', 'ProjectMergeRequestNoteAwardEmojiManager'),)
+ _managers = (("awardemojis", "ProjectMergeRequestNoteAwardEmojiManager"),)
class ProjectMergeRequestNoteManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/merge_requests/%(mr_iid)s/notes'
+ _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/notes"
_obj_cls = ProjectMergeRequestNote
- _from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'}
- _create_attrs = (('body', ), tuple())
- _update_attrs = (('body', ), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
+ _create_attrs = (("body",), tuple())
+ _update_attrs = (("body",), tuple())
-class ProjectMergeRequestDiscussionNote(SaveMixin, ObjectDeleteMixin,
- RESTObject):
+class ProjectMergeRequestDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
-class ProjectMergeRequestDiscussionNoteManager(GetMixin, CreateMixin,
- UpdateMixin, DeleteMixin,
- RESTManager):
- _path = ('/projects/%(project_id)s/merge_requests/%(mr_iid)s/'
- 'discussions/%(discussion_id)s/notes')
+class ProjectMergeRequestDiscussionNoteManager(
+ GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = (
+ "/projects/%(project_id)s/merge_requests/%(mr_iid)s/"
+ "discussions/%(discussion_id)s/notes"
+ )
_obj_cls = ProjectMergeRequestDiscussionNote
- _from_parent_attrs = {'project_id': 'project_id',
- 'mr_iid': 'mr_iid',
- 'discussion_id': 'id'}
- _create_attrs = (('body',), ('created_at',))
- _update_attrs = (('body',), tuple())
+ _from_parent_attrs = {
+ "project_id": "project_id",
+ "mr_iid": "mr_iid",
+ "discussion_id": "id",
+ }
+ _create_attrs = (("body",), ("created_at",))
+ _update_attrs = (("body",), tuple())
class ProjectMergeRequestDiscussion(SaveMixin, RESTObject):
- _managers = (('notes', 'ProjectMergeRequestDiscussionNoteManager'),)
+ _managers = (("notes", "ProjectMergeRequestDiscussionNoteManager"),)
-class ProjectMergeRequestDiscussionManager(RetrieveMixin, CreateMixin,
- UpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/merge_requests/%(mr_iid)s/discussions'
+class ProjectMergeRequestDiscussionManager(
+ RetrieveMixin, CreateMixin, UpdateMixin, RESTManager
+):
+ _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/discussions"
_obj_cls = ProjectMergeRequestDiscussion
- _from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'}
- _create_attrs = (('body',), ('created_at', 'position'))
- _update_attrs = (('resolved',), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
+ _create_attrs = (("body",), ("created_at", "position"))
+ _update_attrs = (("resolved",), tuple())
class ProjectMergeRequestResourceLabelEvent(RESTObject):
@@ -2221,28 +2504,34 @@ class ProjectMergeRequestResourceLabelEvent(RESTObject):
class ProjectMergeRequestResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = ('/projects/%(project_id)s/merge_requests/%(mr_iid)s'
- '/resource_label_events')
+ _path = (
+ "/projects/%(project_id)s/merge_requests/%(mr_iid)s" "/resource_label_events"
+ )
_obj_cls = ProjectMergeRequestResourceLabelEvent
- _from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'}
+ _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
- ParticipantsMixin, SaveMixin, ObjectDeleteMixin,
- RESTObject):
- _id_attr = 'iid'
+class ProjectMergeRequest(
+ SubscribableMixin,
+ TodoMixin,
+ TimeTrackingMixin,
+ ParticipantsMixin,
+ SaveMixin,
+ ObjectDeleteMixin,
+ RESTObject,
+):
+ _id_attr = "iid"
_managers = (
- ('approvals', 'ProjectMergeRequestApprovalManager'),
- ('awardemojis', 'ProjectMergeRequestAwardEmojiManager'),
- ('diffs', 'ProjectMergeRequestDiffManager'),
- ('discussions', 'ProjectMergeRequestDiscussionManager'),
- ('notes', 'ProjectMergeRequestNoteManager'),
- ('resourcelabelevents',
- 'ProjectMergeRequestResourceLabelEventManager'),
+ ("approvals", "ProjectMergeRequestApprovalManager"),
+ ("awardemojis", "ProjectMergeRequestAwardEmojiManager"),
+ ("diffs", "ProjectMergeRequestDiffManager"),
+ ("discussions", "ProjectMergeRequestDiscussionManager"),
+ ("notes", "ProjectMergeRequestNoteManager"),
+ ("resourcelabelevents", "ProjectMergeRequestResourceLabelEventManager"),
)
- @cli.register_custom_action('ProjectMergeRequest')
+ @cli.register_custom_action("ProjectMergeRequest")
@exc.on_http_error(exc.GitlabMROnBuildSuccessError)
def cancel_merge_when_pipeline_succeeds(self, **kwargs):
"""Cancel merge when the pipeline succeeds.
@@ -2256,12 +2545,14 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
request
"""
- path = ('%s/%s/cancel_merge_when_pipeline_succeeds' %
- (self.manager.path, self.get_id()))
+ path = "%s/%s/cancel_merge_when_pipeline_succeeds" % (
+ self.manager.path,
+ self.get_id(),
+ )
server_data = self.manager.gitlab.http_put(path, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('ProjectMergeRequest')
+ @cli.register_custom_action("ProjectMergeRequest")
@exc.on_http_error(exc.GitlabListError)
def closes_issues(self, **kwargs):
"""List issues that will close on merge."
@@ -2281,14 +2572,12 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
Returns:
RESTObjectList: List of issues
"""
- path = '%s/%s/closes_issues' % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False,
- **kwargs)
- manager = ProjectIssueManager(self.manager.gitlab,
- parent=self.manager._parent)
+ path = "%s/%s/closes_issues" % (self.manager.path, self.get_id())
+ data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
+ manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent)
return RESTObjectList(manager, ProjectIssue, data_list)
- @cli.register_custom_action('ProjectMergeRequest')
+ @cli.register_custom_action("ProjectMergeRequest")
@exc.on_http_error(exc.GitlabListError)
def commits(self, **kwargs):
"""List the merge request commits.
@@ -2309,14 +2598,12 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
RESTObjectList: The list of commits
"""
- path = '%s/%s/commits' % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False,
- **kwargs)
- manager = ProjectCommitManager(self.manager.gitlab,
- parent=self.manager._parent)
+ path = "%s/%s/commits" % (self.manager.path, self.get_id())
+ data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
+ manager = ProjectCommitManager(self.manager.gitlab, parent=self.manager._parent)
return RESTObjectList(manager, ProjectCommit, data_list)
- @cli.register_custom_action('ProjectMergeRequest')
+ @cli.register_custom_action("ProjectMergeRequest")
@exc.on_http_error(exc.GitlabListError)
def changes(self, **kwargs):
"""List the merge request changes.
@@ -2331,10 +2618,10 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
Returns:
RESTObjectList: List of changes
"""
- path = '%s/%s/changes' % (self.manager.path, self.get_id())
+ path = "%s/%s/changes" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)
- @cli.register_custom_action('ProjectMergeRequest')
+ @cli.register_custom_action("ProjectMergeRequest")
@exc.on_http_error(exc.GitlabListError)
def pipelines(self, **kwargs):
"""List the merge request pipelines.
@@ -2350,10 +2637,10 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
RESTObjectList: List of changes
"""
- path = '%s/%s/pipelines' % (self.manager.path, self.get_id())
+ path = "%s/%s/pipelines" % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)
- @cli.register_custom_action('ProjectMergeRequest', tuple(), ('sha'))
+ @cli.register_custom_action("ProjectMergeRequest", tuple(), ("sha"))
@exc.on_http_error(exc.GitlabMRApprovalError)
def approve(self, sha=None, **kwargs):
"""Approve the merge request.
@@ -2366,16 +2653,15 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
GitlabAuthenticationError: If authentication is not correct
GitlabMRApprovalError: If the approval failed
"""
- path = '%s/%s/approve' % (self.manager.path, self.get_id())
+ path = "%s/%s/approve" % (self.manager.path, self.get_id())
data = {}
if sha:
- data['sha'] = sha
+ data["sha"] = sha
- server_data = self.manager.gitlab.http_post(path, post_data=data,
- **kwargs)
+ server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('ProjectMergeRequest')
+ @cli.register_custom_action("ProjectMergeRequest")
@exc.on_http_error(exc.GitlabMRApprovalError)
def unapprove(self, **kwargs):
"""Unapprove the merge request.
@@ -2387,22 +2673,29 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
GitlabAuthenticationError: If authentication is not correct
GitlabMRApprovalError: If the unapproval failed
"""
- path = '%s/%s/unapprove' % (self.manager.path, self.get_id())
+ path = "%s/%s/unapprove" % (self.manager.path, self.get_id())
data = {}
- server_data = self.manager.gitlab.http_post(path, post_data=data,
- **kwargs)
+ server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('ProjectMergeRequest', tuple(),
- ('merge_commit_message',
- 'should_remove_source_branch',
- 'merge_when_pipeline_succeeds'))
+ @cli.register_custom_action(
+ "ProjectMergeRequest",
+ tuple(),
+ (
+ "merge_commit_message",
+ "should_remove_source_branch",
+ "merge_when_pipeline_succeeds",
+ ),
+ )
@exc.on_http_error(exc.GitlabMRClosedError)
- def merge(self, merge_commit_message=None,
- should_remove_source_branch=False,
- merge_when_pipeline_succeeds=False,
- **kwargs):
+ def merge(
+ self,
+ merge_commit_message=None,
+ should_remove_source_branch=False,
+ merge_when_pipeline_succeeds=False,
+ **kwargs
+ ):
"""Accept the merge request.
Args:
@@ -2417,47 +2710,78 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
GitlabAuthenticationError: If authentication is not correct
GitlabMRClosedError: If the merge failed
"""
- path = '%s/%s/merge' % (self.manager.path, self.get_id())
+ path = "%s/%s/merge" % (self.manager.path, self.get_id())
data = {}
if merge_commit_message:
- data['merge_commit_message'] = merge_commit_message
+ data["merge_commit_message"] = merge_commit_message
if should_remove_source_branch:
- data['should_remove_source_branch'] = True
+ data["should_remove_source_branch"] = True
if merge_when_pipeline_succeeds:
- data['merge_when_pipeline_succeeds'] = True
+ data["merge_when_pipeline_succeeds"] = True
- server_data = self.manager.gitlab.http_put(path, post_data=data,
- **kwargs)
+ server_data = self.manager.gitlab.http_put(path, post_data=data, **kwargs)
self._update_attrs(server_data)
class ProjectMergeRequestManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/merge_requests'
+ _path = "/projects/%(project_id)s/merge_requests"
_obj_cls = ProjectMergeRequest
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
_create_attrs = (
- ('source_branch', 'target_branch', 'title'),
- ('assignee_id', 'description', 'target_project_id', 'labels',
- 'milestone_id', 'remove_source_branch', 'allow_maintainer_to_push',
- 'squash')
+ ("source_branch", "target_branch", "title"),
+ (
+ "assignee_id",
+ "description",
+ "target_project_id",
+ "labels",
+ "milestone_id",
+ "remove_source_branch",
+ "allow_maintainer_to_push",
+ "squash",
+ ),
)
_update_attrs = (
tuple(),
- ('target_branch', 'assignee_id', 'title', 'description', 'state_event',
- 'labels', 'milestone_id', 'remove_source_branch', 'discussion_locked',
- 'allow_maintainer_to_push', 'squash'))
- _list_filters = ('state', 'order_by', 'sort', 'milestone', 'view',
- 'labels', 'created_after', 'created_before',
- 'updated_after', 'updated_before', 'scope', 'author_id',
- 'assignee_id', 'my_reaction_emoji', 'source_branch',
- 'target_branch', 'search')
- _types = {'labels': types.ListAttribute}
+ (
+ "target_branch",
+ "assignee_id",
+ "title",
+ "description",
+ "state_event",
+ "labels",
+ "milestone_id",
+ "remove_source_branch",
+ "discussion_locked",
+ "allow_maintainer_to_push",
+ "squash",
+ ),
+ )
+ _list_filters = (
+ "state",
+ "order_by",
+ "sort",
+ "milestone",
+ "view",
+ "labels",
+ "created_after",
+ "created_before",
+ "updated_after",
+ "updated_before",
+ "scope",
+ "author_id",
+ "assignee_id",
+ "my_reaction_emoji",
+ "source_branch",
+ "target_branch",
+ "search",
+ )
+ _types = {"labels": types.ListAttribute}
class ProjectMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'title'
+ _short_print_attr = "title"
- @cli.register_custom_action('ProjectMilestone')
+ @cli.register_custom_action("ProjectMilestone")
@exc.on_http_error(exc.GitlabListError)
def issues(self, **kwargs):
"""List issues related to this milestone.
@@ -2478,15 +2802,13 @@ class ProjectMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
RESTObjectList: The list of issues
"""
- path = '%s/%s/issues' % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False,
- **kwargs)
- manager = ProjectIssueManager(self.manager.gitlab,
- parent=self.manager._parent)
+ path = "%s/%s/issues" % (self.manager.path, self.get_id())
+ data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
+ manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent)
# FIXME(gpocentek): the computed manager path is not correct
return RESTObjectList(manager, ProjectIssue, data_list)
- @cli.register_custom_action('ProjectMilestone')
+ @cli.register_custom_action("ProjectMilestone")
@exc.on_http_error(exc.GitlabListError)
def merge_requests(self, **kwargs):
"""List the merge requests related to this milestone.
@@ -2506,29 +2828,32 @@ class ProjectMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
RESTObjectList: The list of merge requests
"""
- path = '%s/%s/merge_requests' % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False,
- **kwargs)
- manager = ProjectMergeRequestManager(self.manager.gitlab,
- parent=self.manager._parent)
+ path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
+ data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
+ manager = ProjectMergeRequestManager(
+ self.manager.gitlab, parent=self.manager._parent
+ )
# FIXME(gpocentek): the computed manager path is not correct
return RESTObjectList(manager, ProjectMergeRequest, data_list)
class ProjectMilestoneManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/milestones'
+ _path = "/projects/%(project_id)s/milestones"
_obj_cls = ProjectMilestone
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('title', ), ('description', 'due_date', 'start_date',
- 'state_event'))
- _update_attrs = (tuple(), ('title', 'description', 'due_date',
- 'start_date', 'state_event'))
- _list_filters = ('iids', 'state', 'search')
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (
+ ("title",),
+ ("description", "due_date", "start_date", "state_event"),
+ )
+ _update_attrs = (
+ tuple(),
+ ("title", "description", "due_date", "start_date", "state_event"),
+ )
+ _list_filters = ("iids", "state", "search")
-class ProjectLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin,
- RESTObject):
- _id_attr = 'name'
+class ProjectLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
+ _id_attr = "name"
# Update without ID, but we need an ID to get from list.
@exc.on_http_error(exc.GitlabUpdateError)
@@ -2551,14 +2876,14 @@ class ProjectLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin,
self._update_attrs(server_data)
-class ProjectLabelManager(ListMixin, CreateMixin, UpdateMixin, DeleteMixin,
- RESTManager):
- _path = '/projects/%(project_id)s/labels'
+class ProjectLabelManager(
+ ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = "/projects/%(project_id)s/labels"
_obj_cls = ProjectLabel
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('name', 'color'), ('description', 'priority'))
- _update_attrs = (('name', ),
- ('new_name', 'color', 'description', 'priority'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("name", "color"), ("description", "priority"))
+ _update_attrs = (("name",), ("new_name", "color", "description", "priority"))
# Delete without ID.
@exc.on_http_error(exc.GitlabDeleteError)
@@ -2573,12 +2898,12 @@ class ProjectLabelManager(ListMixin, CreateMixin, UpdateMixin, DeleteMixin,
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server cannot perform the request
"""
- self.gitlab.http_delete(self.path, query_data={'name': name}, **kwargs)
+ self.gitlab.http_delete(self.path, query_data={"name": name}, **kwargs)
class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = 'file_path'
- _short_print_attr = 'file_path'
+ _id_attr = "file_path"
+ _short_print_attr = "file_path"
def decode(self):
"""Returns the decoded content of the file.
@@ -2604,7 +2929,7 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
"""
self.branch = branch
self.commit_message = commit_message
- self.file_path = self.file_path.replace('/', '%2F')
+ self.file_path = self.file_path.replace("/", "%2F")
super(ProjectFile, self).save(**kwargs)
def delete(self, branch, commit_message, **kwargs):
@@ -2619,21 +2944,24 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server cannot perform the request
"""
- file_path = self.get_id().replace('/', '%2F')
+ file_path = self.get_id().replace("/", "%2F")
self.manager.delete(file_path, branch, commit_message, **kwargs)
-class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin,
- RESTManager):
- _path = '/projects/%(project_id)s/repository/files'
+class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager):
+ _path = "/projects/%(project_id)s/repository/files"
_obj_cls = ProjectFile
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('file_path', 'branch', 'content', 'commit_message'),
- ('encoding', 'author_email', 'author_name'))
- _update_attrs = (('file_path', 'branch', 'content', 'commit_message'),
- ('encoding', 'author_email', 'author_name'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (
+ ("file_path", "branch", "content", "commit_message"),
+ ("encoding", "author_email", "author_name"),
+ )
+ _update_attrs = (
+ ("file_path", "branch", "content", "commit_message"),
+ ("encoding", "author_email", "author_name"),
+ )
- @cli.register_custom_action('ProjectFileManager', ('file_path', 'ref'))
+ @cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
def get(self, file_path, ref, **kwargs):
"""Retrieve a single file.
@@ -2649,13 +2977,14 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin,
Returns:
object: The generated RESTObject
"""
- file_path = file_path.replace('/', '%2F')
+ file_path = file_path.replace("/", "%2F")
return GetMixin.get(self, file_path, ref=ref, **kwargs)
- @cli.register_custom_action('ProjectFileManager',
- ('file_path', 'branch', 'content',
- 'commit_message'),
- ('encoding', 'author_email', 'author_name'))
+ @cli.register_custom_action(
+ "ProjectFileManager",
+ ("file_path", "branch", "content", "commit_message"),
+ ("encoding", "author_email", "author_name"),
+ )
@exc.on_http_error(exc.GitlabCreateError)
def create(self, data, **kwargs):
"""Create a new object.
@@ -2676,8 +3005,8 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin,
self._check_missing_create_attrs(data)
new_data = data.copy()
- file_path = new_data.pop('file_path').replace('/', '%2F')
- path = '%s/%s' % (self.path, file_path)
+ file_path = new_data.pop("file_path").replace("/", "%2F")
+ path = "%s/%s" % (self.path, file_path)
server_data = self.gitlab.http_post(path, post_data=new_data, **kwargs)
return self._obj_cls(self, server_data)
@@ -2699,14 +3028,15 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin,
"""
data = new_data.copy()
- file_path = file_path.replace('/', '%2F')
- data['file_path'] = file_path
- path = '%s/%s' % (self.path, file_path)
+ file_path = file_path.replace("/", "%2F")
+ data["file_path"] = file_path
+ path = "%s/%s" % (self.path, file_path)
self._check_missing_update_attrs(data)
return self.gitlab.http_put(path, post_data=data, **kwargs)
- @cli.register_custom_action('ProjectFileManager', ('file_path', 'branch',
- 'commit_message'))
+ @cli.register_custom_action(
+ "ProjectFileManager", ("file_path", "branch", "commit_message")
+ )
@exc.on_http_error(exc.GitlabDeleteError)
def delete(self, file_path, branch, commit_message, **kwargs):
"""Delete a file on the server.
@@ -2721,14 +3051,15 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin,
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server cannot perform the request
"""
- path = '%s/%s' % (self.path, file_path.replace('/', '%2F'))
- data = {'branch': branch, 'commit_message': commit_message}
+ path = "%s/%s" % (self.path, file_path.replace("/", "%2F"))
+ data = {"branch": branch, "commit_message": commit_message}
self.gitlab.http_delete(path, query_data=data, **kwargs)
- @cli.register_custom_action('ProjectFileManager', ('file_path', 'ref'))
+ @cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
@exc.on_http_error(exc.GitlabGetError)
- def raw(self, file_path, ref, streamed=False, action=None, chunk_size=1024,
- **kwargs):
+ def raw(
+ self, file_path, ref, streamed=False, action=None, chunk_size=1024, **kwargs
+ ):
"""Return the content of a file for a commit.
Args:
@@ -2749,11 +3080,12 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin,
Returns:
str: The file content
"""
- file_path = file_path.replace('/', '%2F').replace('.', '%2E')
- path = '%s/%s/raw' % (self.path, file_path)
- query_data = {'ref': ref}
- result = self.gitlab.http_get(path, query_data=query_data,
- streamed=streamed, raw=True, **kwargs)
+ file_path = file_path.replace("/", "%2F").replace(".", "%2E")
+ path = "%s/%s/raw" % (self.path, file_path)
+ query_data = {"ref": ref}
+ result = self.gitlab.http_get(
+ path, query_data=query_data, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
@@ -2762,17 +3094,16 @@ class ProjectPipelineJob(RESTObject):
class ProjectPipelineJobManager(ListMixin, RESTManager):
- _path = '/projects/%(project_id)s/pipelines/%(pipeline_id)s/jobs'
+ _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/jobs"
_obj_cls = ProjectPipelineJob
- _from_parent_attrs = {'project_id': 'project_id',
- 'pipeline_id': 'id'}
- _list_filters = ('scope',)
+ _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
+ _list_filters = ("scope",)
class ProjectPipeline(RESTObject, RefreshMixin, ObjectDeleteMixin):
- _managers = (('jobs', 'ProjectPipelineJobManager'), )
+ _managers = (("jobs", "ProjectPipelineJobManager"),)
- @cli.register_custom_action('ProjectPipeline')
+ @cli.register_custom_action("ProjectPipeline")
@exc.on_http_error(exc.GitlabPipelineCancelError)
def cancel(self, **kwargs):
"""Cancel the job.
@@ -2784,10 +3115,10 @@ class ProjectPipeline(RESTObject, RefreshMixin, ObjectDeleteMixin):
GitlabAuthenticationError: If authentication is not correct
GitlabPipelineCancelError: If the request failed
"""
- path = '%s/%s/cancel' % (self.manager.path, self.get_id())
+ path = "%s/%s/cancel" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path)
- @cli.register_custom_action('ProjectPipeline')
+ @cli.register_custom_action("ProjectPipeline")
@exc.on_http_error(exc.GitlabPipelineRetryError)
def retry(self, **kwargs):
"""Retry the job.
@@ -2799,18 +3130,26 @@ class ProjectPipeline(RESTObject, RefreshMixin, ObjectDeleteMixin):
GitlabAuthenticationError: If authentication is not correct
GitlabPipelineRetryError: If the request failed
"""
- path = '%s/%s/retry' % (self.manager.path, self.get_id())
+ path = "%s/%s/retry" % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path)
-class ProjectPipelineManager(RetrieveMixin, CreateMixin, DeleteMixin,
- RESTManager):
- _path = '/projects/%(project_id)s/pipelines'
+class ProjectPipelineManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
+ _path = "/projects/%(project_id)s/pipelines"
_obj_cls = ProjectPipeline
- _from_parent_attrs = {'project_id': 'id'}
- _list_filters = ('scope', 'status', 'ref', 'sha', 'yaml_errors', 'name',
- 'username', 'order_by', 'sort')
- _create_attrs = (('ref', ), tuple())
+ _from_parent_attrs = {"project_id": "id"}
+ _list_filters = (
+ "scope",
+ "status",
+ "ref",
+ "sha",
+ "yaml_errors",
+ "name",
+ "username",
+ "order_by",
+ "sort",
+ )
+ _create_attrs = (("ref",), tuple())
def create(self, data, **kwargs):
"""Creates a new object.
@@ -2832,26 +3171,27 @@ class ProjectPipelineManager(RetrieveMixin, CreateMixin, DeleteMixin,
return CreateMixin.create(self, data, path=path, **kwargs)
-class ProjectPipelineScheduleVariable(SaveMixin, ObjectDeleteMixin,
- RESTObject):
- _id_attr = 'key'
+class ProjectPipelineScheduleVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
+ _id_attr = "key"
-class ProjectPipelineScheduleVariableManager(CreateMixin, UpdateMixin,
- DeleteMixin, RESTManager):
- _path = ('/projects/%(project_id)s/pipeline_schedules/'
- '%(pipeline_schedule_id)s/variables')
+class ProjectPipelineScheduleVariableManager(
+ CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = (
+ "/projects/%(project_id)s/pipeline_schedules/"
+ "%(pipeline_schedule_id)s/variables"
+ )
_obj_cls = ProjectPipelineScheduleVariable
- _from_parent_attrs = {'project_id': 'project_id',
- 'pipeline_schedule_id': 'id'}
- _create_attrs = (('key', 'value'), tuple())
- _update_attrs = (('key', 'value'), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "pipeline_schedule_id": "id"}
+ _create_attrs = (("key", "value"), tuple())
+ _update_attrs = (("key", "value"), tuple())
class ProjectPipelineSchedule(SaveMixin, ObjectDeleteMixin, RESTObject):
- _managers = (('variables', 'ProjectPipelineScheduleVariableManager'),)
+ _managers = (("variables", "ProjectPipelineScheduleVariableManager"),)
- @cli.register_custom_action('ProjectPipelineSchedule')
+ @cli.register_custom_action("ProjectPipelineSchedule")
@exc.on_http_error(exc.GitlabOwnershipError)
def take_ownership(self, **kwargs):
"""Update the owner of a pipeline schedule.
@@ -2863,40 +3203,55 @@ class ProjectPipelineSchedule(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabOwnershipError: If the request failed
"""
- path = '%s/%s/take_ownership' % (self.manager.path, self.get_id())
+ path = "%s/%s/take_ownership" % (self.manager.path, self.get_id())
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
class ProjectPipelineScheduleManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/pipeline_schedules'
+ _path = "/projects/%(project_id)s/pipeline_schedules"
_obj_cls = ProjectPipelineSchedule
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('description', 'ref', 'cron'),
- ('cron_timezone', 'active'))
- _update_attrs = (tuple(),
- ('description', 'ref', 'cron', 'cron_timezone', 'active'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("description", "ref", "cron"), ("cron_timezone", "active"))
+ _update_attrs = (tuple(), ("description", "ref", "cron", "cron_timezone", "active"))
class ProjectPushRules(SaveMixin, ObjectDeleteMixin, RESTObject):
_id_attr = None
-class ProjectPushRulesManager(GetWithoutIdMixin, CreateMixin, UpdateMixin,
- DeleteMixin, RESTManager):
- _path = '/projects/%(project_id)s/push_rule'
+class ProjectPushRulesManager(
+ GetWithoutIdMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = "/projects/%(project_id)s/push_rule"
_obj_cls = ProjectPushRules
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (tuple(),
- ('deny_delete_tag', 'member_check',
- 'prevent_secrets', 'commit_message_regex',
- 'branch_name_regex', 'author_email_regex',
- 'file_name_regex', 'max_file_size'))
- _update_attrs = (tuple(),
- ('deny_delete_tag', 'member_check',
- 'prevent_secrets', 'commit_message_regex',
- 'branch_name_regex', 'author_email_regex',
- 'file_name_regex', 'max_file_size'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (
+ tuple(),
+ (
+ "deny_delete_tag",
+ "member_check",
+ "prevent_secrets",
+ "commit_message_regex",
+ "branch_name_regex",
+ "author_email_regex",
+ "file_name_regex",
+ "max_file_size",
+ ),
+ )
+ _update_attrs = (
+ tuple(),
+ (
+ "deny_delete_tag",
+ "member_check",
+ "prevent_secrets",
+ "commit_message_regex",
+ "branch_name_regex",
+ "author_email_regex",
+ "file_name_regex",
+ "max_file_size",
+ ),
+ )
class ProjectSnippetNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
@@ -2904,26 +3259,29 @@ class ProjectSnippetNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
class ProjectSnippetNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = ('/projects/%(project_id)s/snippets/%(snippet_id)s'
- '/notes/%(note_id)s/award_emoji')
+ _path = (
+ "/projects/%(project_id)s/snippets/%(snippet_id)s"
+ "/notes/%(note_id)s/award_emoji"
+ )
_obj_cls = ProjectSnippetNoteAwardEmoji
- _from_parent_attrs = {'project_id': 'project_id',
- 'snippet_id': 'snippet_id',
- 'note_id': 'id'}
- _create_attrs = (('name', ), tuple())
+ _from_parent_attrs = {
+ "project_id": "project_id",
+ "snippet_id": "snippet_id",
+ "note_id": "id",
+ }
+ _create_attrs = (("name",), tuple())
class ProjectSnippetNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- _managers = (('awardemojis', 'ProjectSnippetNoteAwardEmojiManager'),)
+ _managers = (("awardemojis", "ProjectSnippetNoteAwardEmojiManager"),)
class ProjectSnippetNoteManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/snippets/%(snippet_id)s/notes'
+ _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/notes"
_obj_cls = ProjectSnippetNote
- _from_parent_attrs = {'project_id': 'project_id',
- 'snippet_id': 'id'}
- _create_attrs = (('body', ), tuple())
- _update_attrs = (('body', ), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
+ _create_attrs = (("body",), tuple())
+ _update_attrs = (("body",), tuple())
class ProjectSnippetAwardEmoji(ObjectDeleteMixin, RESTObject):
@@ -2931,50 +3289,54 @@ class ProjectSnippetAwardEmoji(ObjectDeleteMixin, RESTObject):
class ProjectSnippetAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/snippets/%(snippet_id)s/award_emoji'
+ _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/award_emoji"
_obj_cls = ProjectSnippetAwardEmoji
- _from_parent_attrs = {'project_id': 'project_id', 'snippet_id': 'id'}
- _create_attrs = (('name', ), tuple())
+ _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
+ _create_attrs = (("name",), tuple())
class ProjectSnippetDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
-class ProjectSnippetDiscussionNoteManager(GetMixin, CreateMixin, UpdateMixin,
- DeleteMixin, RESTManager):
- _path = ('/projects/%(project_id)s/snippets/%(snippet_id)s/'
- 'discussions/%(discussion_id)s/notes')
+class ProjectSnippetDiscussionNoteManager(
+ GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
+):
+ _path = (
+ "/projects/%(project_id)s/snippets/%(snippet_id)s/"
+ "discussions/%(discussion_id)s/notes"
+ )
_obj_cls = ProjectSnippetDiscussionNote
- _from_parent_attrs = {'project_id': 'project_id',
- 'snippet_id': 'snippet_id',
- 'discussion_id': 'id'}
- _create_attrs = (('body',), ('created_at',))
- _update_attrs = (('body',), tuple())
+ _from_parent_attrs = {
+ "project_id": "project_id",
+ "snippet_id": "snippet_id",
+ "discussion_id": "id",
+ }
+ _create_attrs = (("body",), ("created_at",))
+ _update_attrs = (("body",), tuple())
class ProjectSnippetDiscussion(RESTObject):
- _managers = (('notes', 'ProjectSnippetDiscussionNoteManager'),)
+ _managers = (("notes", "ProjectSnippetDiscussionNoteManager"),)
class ProjectSnippetDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = '/projects/%(project_id)s/snippets/%(snippet_id)s/discussions'
+ _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/discussions"
_obj_cls = ProjectSnippetDiscussion
- _from_parent_attrs = {'project_id': 'project_id', 'snippet_id': 'id'}
- _create_attrs = (('body',), ('created_at',))
+ _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
+ _create_attrs = (("body",), ("created_at",))
-class ProjectSnippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin,
- RESTObject):
- _url = '/projects/%(project_id)s/snippets'
- _short_print_attr = 'title'
+class ProjectSnippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
+ _url = "/projects/%(project_id)s/snippets"
+ _short_print_attr = "title"
_managers = (
- ('awardemojis', 'ProjectSnippetAwardEmojiManager'),
- ('discussions', 'ProjectSnippetDiscussionManager'),
- ('notes', 'ProjectSnippetNoteManager'),
+ ("awardemojis", "ProjectSnippetAwardEmojiManager"),
+ ("discussions", "ProjectSnippetDiscussionManager"),
+ ("notes", "ProjectSnippetNoteManager"),
)
- @cli.register_custom_action('ProjectSnippet')
+ @cli.register_custom_action("ProjectSnippet")
@exc.on_http_error(exc.GitlabGetError)
def content(self, streamed=False, action=None, chunk_size=1024, **kwargs):
"""Return the content of a snippet.
@@ -2996,22 +3358,22 @@ class ProjectSnippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin,
str: The snippet content
"""
path = "%s/%s/raw" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, streamed=streamed,
- raw=True, **kwargs)
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
class ProjectSnippetManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/snippets'
+ _path = "/projects/%(project_id)s/snippets"
_obj_cls = ProjectSnippet
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('title', 'file_name', 'code'),
- ('lifetime', 'visibility'))
- _update_attrs = (tuple(), ('title', 'file_name', 'code', 'visibility'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("title", "file_name", "code"), ("lifetime", "visibility"))
+ _update_attrs = (tuple(), ("title", "file_name", "code", "visibility"))
class ProjectTrigger(SaveMixin, ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action('ProjectTrigger')
+ @cli.register_custom_action("ProjectTrigger")
@exc.on_http_error(exc.GitlabOwnershipError)
def take_ownership(self, **kwargs):
"""Update the owner of a trigger.
@@ -3023,17 +3385,17 @@ class ProjectTrigger(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabOwnershipError: If the request failed
"""
- path = '%s/%s/take_ownership' % (self.manager.path, self.get_id())
+ path = "%s/%s/take_ownership" % (self.manager.path, self.get_id())
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
class ProjectTriggerManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/triggers'
+ _path = "/projects/%(project_id)s/triggers"
_obj_cls = ProjectTrigger
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('description', ), tuple())
- _update_attrs = (('description', ), tuple())
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("description",), tuple())
+ _update_attrs = (("description",), tuple())
class ProjectUser(RESTObject):
@@ -3041,22 +3403,22 @@ class ProjectUser(RESTObject):
class ProjectUserManager(ListMixin, RESTManager):
- _path = '/projects/%(project_id)s/users'
+ _path = "/projects/%(project_id)s/users"
_obj_cls = ProjectUser
- _from_parent_attrs = {'project_id': 'id'}
- _list_filters = ('search',)
+ _from_parent_attrs = {"project_id": "id"}
+ _list_filters = ("search",)
class ProjectVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = 'key'
+ _id_attr = "key"
class ProjectVariableManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/variables'
+ _path = "/projects/%(project_id)s/variables"
_obj_cls = ProjectVariable
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('key', 'value'), tuple())
- _update_attrs = (('key', 'value'), tuple())
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("key", "value"), tuple())
+ _update_attrs = (("key", "value"), tuple())
class ProjectService(SaveMixin, ObjectDeleteMixin, RESTObject):
@@ -3064,46 +3426,57 @@ class ProjectService(SaveMixin, ObjectDeleteMixin, RESTObject):
class ProjectServiceManager(GetMixin, UpdateMixin, DeleteMixin, RESTManager):
- _path = '/projects/%(project_id)s/services'
- _from_parent_attrs = {'project_id': 'id'}
+ _path = "/projects/%(project_id)s/services"
+ _from_parent_attrs = {"project_id": "id"}
_obj_cls = ProjectService
_service_attrs = {
- 'asana': (('api_key', ), ('restrict_to_branch', )),
- 'assembla': (('token', ), ('subdomain', )),
- 'bamboo': (('bamboo_url', 'build_key', 'username', 'password'),
- tuple()),
- 'buildkite': (('token', 'project_url'), ('enable_ssl_verification', )),
- 'campfire': (('token', ), ('subdomain', 'room')),
- 'custom-issue-tracker': (('new_issue_url', 'issues_url',
- 'project_url'),
- ('description', 'title')),
- 'drone-ci': (('token', 'drone_url'), ('enable_ssl_verification', )),
- 'emails-on-push': (('recipients', ), ('disable_diffs',
- 'send_from_committer_email')),
- 'builds-email': (('recipients', ), ('add_pusher',
- 'notify_only_broken_builds')),
- 'pipelines-email': (('recipients', ), ('add_pusher',
- 'notify_only_broken_builds')),
- 'external-wiki': (('external_wiki_url', ), tuple()),
- 'flowdock': (('token', ), tuple()),
- 'gemnasium': (('api_key', 'token', ), tuple()),
- 'hipchat': (('token', ), ('color', 'notify', 'room', 'api_version',
- 'server')),
- 'irker': (('recipients', ), ('default_irc_uri', 'server_port',
- 'server_host', 'colorize_messages')),
- 'jira': (('url', 'project_key'),
- ('new_issue_url', 'project_url', 'issues_url', 'api_url',
- 'description', 'username', 'password',
- 'jira_issue_transition_id')),
- 'mattermost': (('webhook',), ('username', 'channel')),
- 'pivotaltracker': (('token', ), tuple()),
- 'pushover': (('api_key', 'user_key', 'priority'), ('device', 'sound')),
- 'redmine': (('new_issue_url', 'project_url', 'issues_url'),
- ('description', )),
- 'slack': (('webhook', ), ('username', 'channel')),
- 'teamcity': (('teamcity_url', 'build_type', 'username', 'password'),
- tuple())
+ "asana": (("api_key",), ("restrict_to_branch",)),
+ "assembla": (("token",), ("subdomain",)),
+ "bamboo": (("bamboo_url", "build_key", "username", "password"), tuple()),
+ "buildkite": (("token", "project_url"), ("enable_ssl_verification",)),
+ "campfire": (("token",), ("subdomain", "room")),
+ "custom-issue-tracker": (
+ ("new_issue_url", "issues_url", "project_url"),
+ ("description", "title"),
+ ),
+ "drone-ci": (("token", "drone_url"), ("enable_ssl_verification",)),
+ "emails-on-push": (
+ ("recipients",),
+ ("disable_diffs", "send_from_committer_email"),
+ ),
+ "builds-email": (("recipients",), ("add_pusher", "notify_only_broken_builds")),
+ "pipelines-email": (
+ ("recipients",),
+ ("add_pusher", "notify_only_broken_builds"),
+ ),
+ "external-wiki": (("external_wiki_url",), tuple()),
+ "flowdock": (("token",), tuple()),
+ "gemnasium": (("api_key", "token"), tuple()),
+ "hipchat": (("token",), ("color", "notify", "room", "api_version", "server")),
+ "irker": (
+ ("recipients",),
+ ("default_irc_uri", "server_port", "server_host", "colorize_messages"),
+ ),
+ "jira": (
+ ("url", "project_key"),
+ (
+ "new_issue_url",
+ "project_url",
+ "issues_url",
+ "api_url",
+ "description",
+ "username",
+ "password",
+ "jira_issue_transition_id",
+ ),
+ ),
+ "mattermost": (("webhook",), ("username", "channel")),
+ "pivotaltracker": (("token",), tuple()),
+ "pushover": (("api_key", "user_key", "priority"), ("device", "sound")),
+ "redmine": (("new_issue_url", "project_url", "issues_url"), ("description",)),
+ "slack": (("webhook",), ("username", "channel")),
+ "teamcity": (("teamcity_url", "build_type", "username", "password"), tuple()),
}
def get(self, id, **kwargs):
@@ -3145,7 +3518,7 @@ class ProjectServiceManager(GetMixin, UpdateMixin, DeleteMixin, RESTManager):
super(ProjectServiceManager, self).update(id, new_data, **kwargs)
self.id = id
- @cli.register_custom_action('ProjectServiceManager')
+ @cli.register_custom_action("ProjectServiceManager")
def available(self, **kwargs):
"""List the services known by python-gitlab.
@@ -3159,11 +3532,10 @@ class ProjectAccessRequest(AccessRequestMixin, ObjectDeleteMixin, RESTObject):
pass
-class ProjectAccessRequestManager(ListMixin, CreateMixin, DeleteMixin,
- RESTManager):
- _path = '/projects/%(project_id)s/access_requests'
+class ProjectAccessRequestManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
+ _path = "/projects/%(project_id)s/access_requests"
_obj_cls = ProjectAccessRequest
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
class ProjectApproval(SaveMixin, RESTObject):
@@ -3171,12 +3543,17 @@ class ProjectApproval(SaveMixin, RESTObject):
class ProjectApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/approvals'
+ _path = "/projects/%(project_id)s/approvals"
_obj_cls = ProjectApproval
- _from_parent_attrs = {'project_id': 'id'}
- _update_attrs = (tuple(),
- ('approvals_before_merge', 'reset_approvals_on_push',
- 'disable_overriding_approvers_per_merge_request'))
+ _from_parent_attrs = {"project_id": "id"}
+ _update_attrs = (
+ tuple(),
+ (
+ "approvals_before_merge",
+ "reset_approvals_on_push",
+ "disable_overriding_approvers_per_merge_request",
+ ),
+ )
_update_uses_post = True
@exc.on_http_error(exc.GitlabUpdateError)
@@ -3192,9 +3569,8 @@ class ProjectApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
GitlabUpdateError: If the server failed to perform the request
"""
- path = '/projects/%s/approvers' % self._parent.get_id()
- data = {'approver_ids': approver_ids,
- 'approver_group_ids': approver_group_ids}
+ path = "/projects/%s/approvers" % self._parent.get_id()
+ data = {"approver_ids": approver_ids, "approver_group_ids": approver_group_ids}
self.gitlab.http_put(path, post_data=data, **kwargs)
@@ -3203,24 +3579,31 @@ class ProjectDeployment(RESTObject):
class ProjectDeploymentManager(RetrieveMixin, RESTManager):
- _path = '/projects/%(project_id)s/deployments'
+ _path = "/projects/%(project_id)s/deployments"
_obj_cls = ProjectDeployment
- _from_parent_attrs = {'project_id': 'id'}
- _list_filters = ('order_by', 'sort')
+ _from_parent_attrs = {"project_id": "id"}
+ _list_filters = ("order_by", "sort")
class ProjectProtectedBranch(ObjectDeleteMixin, RESTObject):
- _id_attr = 'name'
+ _id_attr = "name"
class ProjectProtectedBranchManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/protected_branches'
+ _path = "/projects/%(project_id)s/protected_branches"
_obj_cls = ProjectProtectedBranch
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('name', ),
- ('push_access_level', 'merge_access_level',
- 'unprotect_access_level', 'allowed_to_push',
- 'allowed_to_merge', 'allowed_to_unprotect'))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (
+ ("name",),
+ (
+ "push_access_level",
+ "merge_access_level",
+ "unprotect_access_level",
+ "allowed_to_push",
+ "allowed_to_merge",
+ "allowed_to_unprotect",
+ ),
+ )
class ProjectRunner(ObjectDeleteMixin, RESTObject):
@@ -3228,30 +3611,30 @@ class ProjectRunner(ObjectDeleteMixin, RESTObject):
class ProjectRunnerManager(NoUpdateMixin, RESTManager):
- _path = '/projects/%(project_id)s/runners'
+ _path = "/projects/%(project_id)s/runners"
_obj_cls = ProjectRunner
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('runner_id', ), tuple())
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("runner_id",), tuple())
class ProjectWiki(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = 'slug'
- _short_print_attr = 'slug'
+ _id_attr = "slug"
+ _short_print_attr = "slug"
class ProjectWikiManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/wikis'
+ _path = "/projects/%(project_id)s/wikis"
_obj_cls = ProjectWiki
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (('title', 'content'), ('format', ))
- _update_attrs = (tuple(), ('title', 'content', 'format'))
- _list_filters = ('with_content', )
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (("title", "content"), ("format",))
+ _update_attrs = (tuple(), ("title", "content", "format"))
+ _list_filters = ("with_content",)
class ProjectExport(RefreshMixin, RESTObject):
_id_attr = None
- @cli.register_custom_action('ProjectExport')
+ @cli.register_custom_action("ProjectExport")
@exc.on_http_error(exc.GitlabGetError)
def download(self, streamed=False, action=None, chunk_size=1024, **kwargs):
"""Download the archive of a project export.
@@ -3272,17 +3655,18 @@ class ProjectExport(RefreshMixin, RESTObject):
Returns:
str: The blob content if streamed is False, None otherwise
"""
- path = '/projects/%s/export/download' % self.project_id
- result = self.manager.gitlab.http_get(path, streamed=streamed,
- raw=True, **kwargs)
+ path = "/projects/%s/export/download" % self.project_id
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
class ProjectExportManager(GetWithoutIdMixin, CreateMixin, RESTManager):
- _path = '/projects/%(project_id)s/export'
+ _path = "/projects/%(project_id)s/export"
_obj_cls = ProjectExport
- _from_parent_attrs = {'project_id': 'id'}
- _create_attrs = (tuple(), ('description',))
+ _from_parent_attrs = {"project_id": "id"}
+ _create_attrs = (tuple(), ("description",))
class ProjectImport(RefreshMixin, RESTObject):
@@ -3290,59 +3674,59 @@ class ProjectImport(RefreshMixin, RESTObject):
class ProjectImportManager(GetWithoutIdMixin, RESTManager):
- _path = '/projects/%(project_id)s/import'
+ _path = "/projects/%(project_id)s/import"
_obj_cls = ProjectImport
- _from_parent_attrs = {'project_id': 'id'}
+ _from_parent_attrs = {"project_id": "id"}
class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = 'path'
+ _short_print_attr = "path"
_managers = (
- ('accessrequests', 'ProjectAccessRequestManager'),
- ('approvals', 'ProjectApprovalManager'),
- ('badges', 'ProjectBadgeManager'),
- ('boards', 'ProjectBoardManager'),
- ('branches', 'ProjectBranchManager'),
- ('jobs', 'ProjectJobManager'),
- ('commits', 'ProjectCommitManager'),
- ('customattributes', 'ProjectCustomAttributeManager'),
- ('deployments', 'ProjectDeploymentManager'),
- ('environments', 'ProjectEnvironmentManager'),
- ('events', 'ProjectEventManager'),
- ('exports', 'ProjectExportManager'),
- ('files', 'ProjectFileManager'),
- ('forks', 'ProjectForkManager'),
- ('hooks', 'ProjectHookManager'),
- ('keys', 'ProjectKeyManager'),
- ('imports', 'ProjectImportManager'),
- ('issues', 'ProjectIssueManager'),
- ('labels', 'ProjectLabelManager'),
- ('members', 'ProjectMemberManager'),
- ('mergerequests', 'ProjectMergeRequestManager'),
- ('milestones', 'ProjectMilestoneManager'),
- ('notes', 'ProjectNoteManager'),
- ('notificationsettings', 'ProjectNotificationSettingsManager'),
- ('pagesdomains', 'ProjectPagesDomainManager'),
- ('pipelines', 'ProjectPipelineManager'),
- ('protectedbranches', 'ProjectProtectedBranchManager'),
- ('protectedtags', 'ProjectProtectedTagManager'),
- ('pipelineschedules', 'ProjectPipelineScheduleManager'),
- ('pushrules', 'ProjectPushRulesManager'),
- ('releases', 'ProjectReleaseManager'),
- ('repositories', 'ProjectRegistryRepositoryManager'),
- ('runners', 'ProjectRunnerManager'),
- ('services', 'ProjectServiceManager'),
- ('snippets', 'ProjectSnippetManager'),
- ('tags', 'ProjectTagManager'),
- ('users', 'ProjectUserManager'),
- ('triggers', 'ProjectTriggerManager'),
- ('variables', 'ProjectVariableManager'),
- ('wikis', 'ProjectWikiManager'),
+ ("accessrequests", "ProjectAccessRequestManager"),
+ ("approvals", "ProjectApprovalManager"),
+ ("badges", "ProjectBadgeManager"),
+ ("boards", "ProjectBoardManager"),
+ ("branches", "ProjectBranchManager"),
+ ("jobs", "ProjectJobManager"),
+ ("commits", "ProjectCommitManager"),
+ ("customattributes", "ProjectCustomAttributeManager"),
+ ("deployments", "ProjectDeploymentManager"),
+ ("environments", "ProjectEnvironmentManager"),
+ ("events", "ProjectEventManager"),
+ ("exports", "ProjectExportManager"),
+ ("files", "ProjectFileManager"),
+ ("forks", "ProjectForkManager"),
+ ("hooks", "ProjectHookManager"),
+ ("keys", "ProjectKeyManager"),
+ ("imports", "ProjectImportManager"),
+ ("issues", "ProjectIssueManager"),
+ ("labels", "ProjectLabelManager"),
+ ("members", "ProjectMemberManager"),
+ ("mergerequests", "ProjectMergeRequestManager"),
+ ("milestones", "ProjectMilestoneManager"),
+ ("notes", "ProjectNoteManager"),
+ ("notificationsettings", "ProjectNotificationSettingsManager"),
+ ("pagesdomains", "ProjectPagesDomainManager"),
+ ("pipelines", "ProjectPipelineManager"),
+ ("protectedbranches", "ProjectProtectedBranchManager"),
+ ("protectedtags", "ProjectProtectedTagManager"),
+ ("pipelineschedules", "ProjectPipelineScheduleManager"),
+ ("pushrules", "ProjectPushRulesManager"),
+ ("releases", "ProjectReleaseManager"),
+ ("repositories", "ProjectRegistryRepositoryManager"),
+ ("runners", "ProjectRunnerManager"),
+ ("services", "ProjectServiceManager"),
+ ("snippets", "ProjectSnippetManager"),
+ ("tags", "ProjectTagManager"),
+ ("users", "ProjectUserManager"),
+ ("triggers", "ProjectTriggerManager"),
+ ("variables", "ProjectVariableManager"),
+ ("wikis", "ProjectWikiManager"),
)
- @cli.register_custom_action('Project', tuple(), ('path', 'ref'))
+ @cli.register_custom_action("Project", tuple(), ("path", "ref"))
@exc.on_http_error(exc.GitlabGetError)
- def repository_tree(self, path='', ref='', recursive=False, **kwargs):
+ def repository_tree(self, path="", ref="", recursive=False, **kwargs):
"""Return a list of files in the repository.
Args:
@@ -3363,16 +3747,15 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
list: The representation of the tree
"""
- gl_path = '/projects/%s/repository/tree' % self.get_id()
- query_data = {'recursive': recursive}
+ gl_path = "/projects/%s/repository/tree" % self.get_id()
+ query_data = {"recursive": recursive}
if path:
- query_data['path'] = path
+ query_data["path"] = path
if ref:
- query_data['ref'] = ref
- return self.manager.gitlab.http_list(gl_path, query_data=query_data,
- **kwargs)
+ query_data["ref"] = ref
+ return self.manager.gitlab.http_list(gl_path, query_data=query_data, **kwargs)
- @cli.register_custom_action('Project', ('sha', ))
+ @cli.register_custom_action("Project", ("sha",))
@exc.on_http_error(exc.GitlabGetError)
def repository_blob(self, sha, **kwargs):
"""Return a file by blob SHA.
@@ -3389,13 +3772,14 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
dict: The blob content and metadata
"""
- path = '/projects/%s/repository/blobs/%s' % (self.get_id(), sha)
+ path = "/projects/%s/repository/blobs/%s" % (self.get_id(), sha)
return self.manager.gitlab.http_get(path, **kwargs)
- @cli.register_custom_action('Project', ('sha', ))
+ @cli.register_custom_action("Project", ("sha",))
@exc.on_http_error(exc.GitlabGetError)
- def repository_raw_blob(self, sha, streamed=False, action=None,
- chunk_size=1024, **kwargs):
+ def repository_raw_blob(
+ self, sha, streamed=False, action=None, chunk_size=1024, **kwargs
+ ):
"""Return the raw file contents for a blob.
Args:
@@ -3415,12 +3799,13 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
str: The blob content if streamed is False, None otherwise
"""
- path = '/projects/%s/repository/blobs/%s/raw' % (self.get_id(), sha)
- result = self.manager.gitlab.http_get(path, streamed=streamed,
- raw=True, **kwargs)
+ path = "/projects/%s/repository/blobs/%s/raw" % (self.get_id(), sha)
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
- @cli.register_custom_action('Project', ('from_', 'to'))
+ @cli.register_custom_action("Project", ("from_", "to"))
@exc.on_http_error(exc.GitlabGetError)
def repository_compare(self, from_, to, **kwargs):
"""Return a diff between two branches/commits.
@@ -3437,12 +3822,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
str: The diff
"""
- path = '/projects/%s/repository/compare' % self.get_id()
- query_data = {'from': from_, 'to': to}
- return self.manager.gitlab.http_get(path, query_data=query_data,
- **kwargs)
+ path = "/projects/%s/repository/compare" % self.get_id()
+ query_data = {"from": from_, "to": to}
+ return self.manager.gitlab.http_get(path, query_data=query_data, **kwargs)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabGetError)
def repository_contributors(self, **kwargs):
"""Return a list of contributors for the project.
@@ -3462,13 +3846,14 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
list: The contributors
"""
- path = '/projects/%s/repository/contributors' % self.get_id()
+ path = "/projects/%s/repository/contributors" % self.get_id()
return self.manager.gitlab.http_list(path, **kwargs)
- @cli.register_custom_action('Project', tuple(), ('sha', ))
+ @cli.register_custom_action("Project", tuple(), ("sha",))
@exc.on_http_error(exc.GitlabListError)
- def repository_archive(self, sha=None, streamed=False, action=None,
- chunk_size=1024, **kwargs):
+ def repository_archive(
+ self, sha=None, streamed=False, action=None, chunk_size=1024, **kwargs
+ ):
"""Return a tarball of the repository.
Args:
@@ -3488,16 +3873,16 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
str: The binary data of the archive
"""
- path = '/projects/%s/repository/archive' % self.get_id()
+ path = "/projects/%s/repository/archive" % self.get_id()
query_data = {}
if sha:
- query_data['sha'] = sha
- result = self.manager.gitlab.http_get(path, query_data=query_data,
- raw=True, streamed=streamed,
- **kwargs)
+ query_data["sha"] = sha
+ result = self.manager.gitlab.http_get(
+ path, query_data=query_data, raw=True, streamed=streamed, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
- @cli.register_custom_action('Project', ('forked_from_id', ))
+ @cli.register_custom_action("Project", ("forked_from_id",))
@exc.on_http_error(exc.GitlabCreateError)
def create_fork_relation(self, forked_from_id, **kwargs):
"""Create a forked from/to relation between existing projects.
@@ -3510,10 +3895,10 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the relation could not be created
"""
- path = '/projects/%s/fork/%s' % (self.get_id(), forked_from_id)
+ path = "/projects/%s/fork/%s" % (self.get_id(), forked_from_id)
self.manager.gitlab.http_post(path, **kwargs)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabDeleteError)
def delete_fork_relation(self, **kwargs):
"""Delete a forked relation between existing projects.
@@ -3525,10 +3910,10 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server failed to perform the request
"""
- path = '/projects/%s/fork' % self.get_id()
+ path = "/projects/%s/fork" % self.get_id()
self.manager.gitlab.http_delete(path, **kwargs)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabDeleteError)
def delete_merged_branches(self, **kwargs):
"""Delete merged branches.
@@ -3540,10 +3925,10 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server failed to perform the request
"""
- path = '/projects/%s/repository/merged_branches' % self.get_id()
+ path = "/projects/%s/repository/merged_branches" % self.get_id()
self.manager.gitlab.http_delete(path, **kwargs)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabGetError)
def languages(self, **kwargs):
"""Get languages used in the project with percentage value.
@@ -3555,10 +3940,10 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabGetError: If the server failed to perform the request
"""
- path = '/projects/%s/languages' % self.get_id()
+ path = "/projects/%s/languages" % self.get_id()
return self.manager.gitlab.http_get(path, **kwargs)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabCreateError)
def star(self, **kwargs):
"""Star a project.
@@ -3570,11 +3955,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the server failed to perform the request
"""
- path = '/projects/%s/star' % self.get_id()
+ path = "/projects/%s/star" % self.get_id()
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabDeleteError)
def unstar(self, **kwargs):
"""Unstar a project.
@@ -3586,11 +3971,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server failed to perform the request
"""
- path = '/projects/%s/unstar' % self.get_id()
+ path = "/projects/%s/unstar" % self.get_id()
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabCreateError)
def archive(self, **kwargs):
"""Archive a project.
@@ -3602,11 +3987,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the server failed to perform the request
"""
- path = '/projects/%s/archive' % self.get_id()
+ path = "/projects/%s/archive" % self.get_id()
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabDeleteError)
def unarchive(self, **kwargs):
"""Unarchive a project.
@@ -3618,12 +4003,13 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server failed to perform the request
"""
- path = '/projects/%s/unarchive' % self.get_id()
+ path = "/projects/%s/unarchive" % self.get_id()
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('Project', ('group_id', 'group_access'),
- ('expires_at', ))
+ @cli.register_custom_action(
+ "Project", ("group_id", "group_access"), ("expires_at",)
+ )
@exc.on_http_error(exc.GitlabCreateError)
def share(self, group_id, group_access, expires_at=None, **kwargs):
"""Share the project with a group.
@@ -3637,13 +4023,15 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the server failed to perform the request
"""
- path = '/projects/%s/share' % self.get_id()
- data = {'group_id': group_id,
- 'group_access': group_access,
- 'expires_at': expires_at}
+ path = "/projects/%s/share" % self.get_id()
+ data = {
+ "group_id": group_id,
+ "group_access": group_access,
+ "expires_at": expires_at,
+ }
self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- @cli.register_custom_action('Project', ('group_id', ))
+ @cli.register_custom_action("Project", ("group_id",))
@exc.on_http_error(exc.GitlabDeleteError)
def unshare(self, group_id, **kwargs):
"""Delete a shared project link within a group.
@@ -3656,11 +4044,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabDeleteError: If the server failed to perform the request
"""
- path = '/projects/%s/share/%s' % (self.get_id(), group_id)
+ path = "/projects/%s/share/%s" % (self.get_id(), group_id)
self.manager.gitlab.http_delete(path, **kwargs)
# variables not supported in CLI
- @cli.register_custom_action('Project', ('ref', 'token'))
+ @cli.register_custom_action("Project", ("ref", "token"))
@exc.on_http_error(exc.GitlabCreateError)
def trigger_pipeline(self, ref, token, variables={}, **kwargs):
"""Trigger a CI build.
@@ -3677,13 +4065,12 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the server failed to perform the request
"""
- path = '/projects/%s/trigger/pipeline' % self.get_id()
- post_data = {'ref': ref, 'token': token, 'variables': variables}
- attrs = self.manager.gitlab.http_post(
- path, post_data=post_data, **kwargs)
+ path = "/projects/%s/trigger/pipeline" % self.get_id()
+ post_data = {"ref": ref, "token": token, "variables": variables}
+ attrs = self.manager.gitlab.http_post(path, post_data=post_data, **kwargs)
return ProjectPipeline(self.pipelines, attrs)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabHousekeepingError)
def housekeeping(self, **kwargs):
"""Start the housekeeping task.
@@ -3696,11 +4083,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabHousekeepingError: If the server failed to perform the
request
"""
- path = '/projects/%s/housekeeping' % self.get_id()
+ path = "/projects/%s/housekeeping" % self.get_id()
self.manager.gitlab.http_post(path, **kwargs)
# see #56 - add file attachment features
- @cli.register_custom_action('Project', ('filename', 'filepath'))
+ @cli.register_custom_action("Project", ("filename", "filepath"))
@exc.on_http_error(exc.GitlabUploadError)
def upload(self, filename, filedata=None, filepath=None, **kwargs):
"""Upload the specified file into the project.
@@ -3738,24 +4125,17 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
with open(filepath, "rb") as f:
filedata = f.read()
- url = ('/projects/%(id)s/uploads' % {
- 'id': self.id,
- })
- file_info = {
- 'file': (filename, filedata),
- }
+ url = "/projects/%(id)s/uploads" % {"id": self.id}
+ file_info = {"file": (filename, filedata)}
data = self.manager.gitlab.http_post(url, files=file_info)
- return {
- "alt": data['alt'],
- "url": data['url'],
- "markdown": data['markdown']
- }
+ return {"alt": data["alt"], "url": data["url"], "markdown": data["markdown"]}
- @cli.register_custom_action('Project', optional=('wiki',))
+ @cli.register_custom_action("Project", optional=("wiki",))
@exc.on_http_error(exc.GitlabGetError)
- def snapshot(self, wiki=False, streamed=False, action=None,
- chunk_size=1024, **kwargs):
+ def snapshot(
+ self, wiki=False, streamed=False, action=None, chunk_size=1024, **kwargs
+ ):
"""Return a snapshot of the repository.
Args:
@@ -3775,12 +4155,13 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
str: The uncompressed tar archive of the repository
"""
- path = '/projects/%s/snapshot' % self.get_id()
- result = self.manager.gitlab.http_get(path, streamed=streamed,
- raw=True, **kwargs)
+ path = "/projects/%s/snapshot" % self.get_id()
+ result = self.manager.gitlab.http_get(
+ path, streamed=streamed, raw=True, **kwargs
+ )
return utils.response_content(result, streamed, action, chunk_size)
- @cli.register_custom_action('Project', ('scope', 'search'))
+ @cli.register_custom_action("Project", ("scope", "search"))
@exc.on_http_error(exc.GitlabSearchError)
def search(self, scope, search, **kwargs):
"""Search the project resources matching the provided string.'
@@ -3797,11 +4178,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
GitlabList: A list of dicts describing the resources found.
"""
- data = {'scope': scope, 'search': search}
- path = '/projects/%s/search' % self.get_id()
+ data = {"scope": scope, "search": search}
+ path = "/projects/%s/search" % self.get_id()
return self.manager.gitlab.http_list(path, query_data=data, **kwargs)
- @cli.register_custom_action('Project')
+ @cli.register_custom_action("Project")
@exc.on_http_error(exc.GitlabCreateError)
def mirror_pull(self, **kwargs):
"""Start the pull mirroring process for the project.
@@ -3813,10 +4194,10 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabCreateError: If the server failed to perform the request
"""
- path = '/projects/%s/mirror/pull' % self.get_id()
+ path = "/projects/%s/mirror/pull" % self.get_id()
self.manager.gitlab.http_post(path, **kwargs)
- @cli.register_custom_action('Project', ('to_namespace', ))
+ @cli.register_custom_action("Project", ("to_namespace",))
@exc.on_http_error(exc.GitlabTransferProjectError)
def transfer_project(self, to_namespace, **kwargs):
"""Transfer a project to the given namespace ID
@@ -3830,44 +4211,97 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabTransferProjectError: If the project could not be transfered
"""
- path = '/projects/%s/transfer' % (self.id,)
- self.manager.gitlab.http_put(path,
- post_data={"namespace": to_namespace},
- **kwargs)
+ path = "/projects/%s/transfer" % (self.id,)
+ self.manager.gitlab.http_put(
+ path, post_data={"namespace": to_namespace}, **kwargs
+ )
class ProjectManager(CRUDMixin, RESTManager):
- _path = '/projects'
+ _path = "/projects"
_obj_cls = Project
_create_attrs = (
tuple(),
- ('name', 'path', 'namespace_id', 'description', 'issues_enabled',
- 'merge_requests_enabled', 'jobs_enabled', 'wiki_enabled',
- 'snippets_enabled', 'resolve_outdated_diff_discussions',
- 'container_registry_enabled', 'shared_runners_enabled', 'visibility',
- 'import_url', 'public_jobs', 'only_allow_merge_if_pipeline_succeeds',
- 'only_allow_merge_if_all_discussions_are_resolved', 'merge_method',
- 'lfs_enabled', 'request_access_enabled', 'tag_list', 'avatar',
- 'printing_merge_request_link_enabled', 'ci_config_path')
+ (
+ "name",
+ "path",
+ "namespace_id",
+ "description",
+ "issues_enabled",
+ "merge_requests_enabled",
+ "jobs_enabled",
+ "wiki_enabled",
+ "snippets_enabled",
+ "resolve_outdated_diff_discussions",
+ "container_registry_enabled",
+ "shared_runners_enabled",
+ "visibility",
+ "import_url",
+ "public_jobs",
+ "only_allow_merge_if_pipeline_succeeds",
+ "only_allow_merge_if_all_discussions_are_resolved",
+ "merge_method",
+ "lfs_enabled",
+ "request_access_enabled",
+ "tag_list",
+ "avatar",
+ "printing_merge_request_link_enabled",
+ "ci_config_path",
+ ),
)
_update_attrs = (
tuple(),
- ('name', 'path', 'default_branch', 'description', 'issues_enabled',
- 'merge_requests_enabled', 'jobs_enabled', 'wiki_enabled',
- 'snippets_enabled', 'resolve_outdated_diff_discussions',
- 'container_registry_enabled', 'shared_runners_enabled', 'visibility',
- 'import_url', 'public_jobs', 'only_allow_merge_if_pipeline_succeeds',
- 'only_allow_merge_if_all_discussions_are_resolved', 'merge_method',
- 'lfs_enabled', 'request_access_enabled', 'tag_list', 'avatar',
- 'ci_config_path')
+ (
+ "name",
+ "path",
+ "default_branch",
+ "description",
+ "issues_enabled",
+ "merge_requests_enabled",
+ "jobs_enabled",
+ "wiki_enabled",
+ "snippets_enabled",
+ "resolve_outdated_diff_discussions",
+ "container_registry_enabled",
+ "shared_runners_enabled",
+ "visibility",
+ "import_url",
+ "public_jobs",
+ "only_allow_merge_if_pipeline_succeeds",
+ "only_allow_merge_if_all_discussions_are_resolved",
+ "merge_method",
+ "lfs_enabled",
+ "request_access_enabled",
+ "tag_list",
+ "avatar",
+ "ci_config_path",
+ ),
+ )
+ _list_filters = (
+ "search",
+ "owned",
+ "starred",
+ "archived",
+ "visibility",
+ "order_by",
+ "sort",
+ "simple",
+ "membership",
+ "statistics",
+ "with_issues_enabled",
+ "with_merge_requests_enabled",
+ "with_custom_attributes",
)
- _list_filters = ('search', 'owned', 'starred', 'archived', 'visibility',
- 'order_by', 'sort', 'simple', 'membership', 'statistics',
- 'with_issues_enabled', 'with_merge_requests_enabled',
- 'with_custom_attributes')
- def import_project(self, file, path, namespace=None, overwrite=False,
- override_params=None, **kwargs):
+ def import_project(
+ self,
+ file,
+ path,
+ namespace=None,
+ overwrite=False,
+ override_params=None,
+ **kwargs
+ ):
"""Import a project from an archive file.
Args:
@@ -3887,20 +4321,16 @@ class ProjectManager(CRUDMixin, RESTManager):
Returns:
dict: A representation of the import status.
"""
- files = {
- 'file': ('file.tar.gz', file)
- }
- data = {
- 'path': path,
- 'overwrite': overwrite
- }
+ files = {"file": ("file.tar.gz", file)}
+ data = {"path": path, "overwrite": overwrite}
if override_params:
for k, v in override_params.items():
- data['override_params[%s]' % k] = v
+ data["override_params[%s]" % k] = v
if namespace:
- data['namespace'] = namespace
- return self.gitlab.http_post('/projects/import', post_data=data,
- files=files, **kwargs)
+ data["namespace"] = namespace
+ return self.gitlab.http_post(
+ "/projects/import", post_data=data, files=files, **kwargs
+ )
class RunnerJob(RESTObject):
@@ -3908,28 +4338,46 @@ class RunnerJob(RESTObject):
class RunnerJobManager(ListMixin, RESTManager):
- _path = '/runners/%(runner_id)s/jobs'
+ _path = "/runners/%(runner_id)s/jobs"
_obj_cls = RunnerJob
- _from_parent_attrs = {'runner_id': 'id'}
- _list_filters = ('status',)
+ _from_parent_attrs = {"runner_id": "id"}
+ _list_filters = ("status",)
class Runner(SaveMixin, ObjectDeleteMixin, RESTObject):
- _managers = (('jobs', 'RunnerJobManager'),)
+ _managers = (("jobs", "RunnerJobManager"),)
class RunnerManager(CRUDMixin, RESTManager):
- _path = '/runners'
+ _path = "/runners"
_obj_cls = Runner
- _list_filters = ('scope', )
- _create_attrs = (('token',), ('description', 'info', 'active', 'locked',
- 'run_untagged', 'tag_list',
- 'maximum_timeout'))
- _update_attrs = (tuple(), ('description', 'active', 'tag_list',
- 'run_untagged', 'locked', 'access_level',
- 'maximum_timeout'))
-
- @cli.register_custom_action('RunnerManager', tuple(), ('scope', ))
+ _list_filters = ("scope",)
+ _create_attrs = (
+ ("token",),
+ (
+ "description",
+ "info",
+ "active",
+ "locked",
+ "run_untagged",
+ "tag_list",
+ "maximum_timeout",
+ ),
+ )
+ _update_attrs = (
+ tuple(),
+ (
+ "description",
+ "active",
+ "tag_list",
+ "run_untagged",
+ "locked",
+ "access_level",
+ "maximum_timeout",
+ ),
+ )
+
+ @cli.register_custom_action("RunnerManager", tuple(), ("scope",))
@exc.on_http_error(exc.GitlabListError)
def all(self, scope=None, **kwargs):
"""List all the runners.
@@ -3951,13 +4399,13 @@ class RunnerManager(CRUDMixin, RESTManager):
Returns:
list(Runner): a list of runners matching the scope.
"""
- path = '/runners/all'
+ path = "/runners/all"
query_data = {}
if scope is not None:
- query_data['scope'] = scope
+ query_data["scope"] = scope
return self.gitlab.http_list(path, query_data, **kwargs)
- @cli.register_custom_action('RunnerManager', ('token',))
+ @cli.register_custom_action("RunnerManager", ("token",))
@exc.on_http_error(exc.GitlabVerifyError)
def verify(self, token, **kwargs):
"""Validates authentication credentials for a registered Runner.
@@ -3970,13 +4418,13 @@ class RunnerManager(CRUDMixin, RESTManager):
GitlabAuthenticationError: If authentication is not correct
GitlabVerifyError: If the server failed to verify the token
"""
- path = '/runners/verify'
- post_data = {'token': token}
+ path = "/runners/verify"
+ post_data = {"token": token}
self.gitlab.http_post(path, post_data=post_data, **kwargs)
class Todo(ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action('Todo')
+ @cli.register_custom_action("Todo")
@exc.on_http_error(exc.GitlabTodoError)
def mark_as_done(self, **kwargs):
"""Mark the todo as done.
@@ -3988,17 +4436,17 @@ class Todo(ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabTodoError: If the server failed to perform the request
"""
- path = '%s/%s/mark_as_done' % (self.manager.path, self.id)
+ path = "%s/%s/mark_as_done" % (self.manager.path, self.id)
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
class TodoManager(ListMixin, DeleteMixin, RESTManager):
- _path = '/todos'
+ _path = "/todos"
_obj_cls = Todo
- _list_filters = ('action', 'author_id', 'project_id', 'state', 'type')
+ _list_filters = ("action", "author_id", "project_id", "state", "type")
- @cli.register_custom_action('TodoManager')
+ @cli.register_custom_action("TodoManager")
@exc.on_http_error(exc.GitlabTodoError)
def mark_all_as_done(self, **kwargs):
"""Mark all the todos as done.
@@ -4013,7 +4461,7 @@ class TodoManager(ListMixin, DeleteMixin, RESTManager):
Returns:
int: The number of todos maked done
"""
- result = self.gitlab.http_post('/todos/mark_as_done', **kwargs)
+ result = self.gitlab.http_post("/todos/mark_as_done", **kwargs)
try:
return int(result)
except ValueError:
@@ -4021,7 +4469,7 @@ class TodoManager(ListMixin, DeleteMixin, RESTManager):
class GeoNode(SaveMixin, ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action('GeoNode')
+ @cli.register_custom_action("GeoNode")
@exc.on_http_error(exc.GitlabRepairError)
def repair(self, **kwargs):
"""Repair the OAuth authentication of the geo node.
@@ -4033,11 +4481,11 @@ class GeoNode(SaveMixin, ObjectDeleteMixin, RESTObject):
GitlabAuthenticationError: If authentication is not correct
GitlabRepairError: If the server failed to perform the request
"""
- path = '/geo_nodes/%s/repair' % self.get_id()
+ path = "/geo_nodes/%s/repair" % self.get_id()
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
- @cli.register_custom_action('GeoNode')
+ @cli.register_custom_action("GeoNode")
@exc.on_http_error(exc.GitlabGetError)
def status(self, **kwargs):
"""Get the status of the geo node.
@@ -4052,17 +4500,19 @@ class GeoNode(SaveMixin, ObjectDeleteMixin, RESTObject):
Returns:
dict: The status of the geo node
"""
- path = '/geo_nodes/%s/status' % self.get_id()
+ path = "/geo_nodes/%s/status" % self.get_id()
return self.manager.gitlab.http_get(path, **kwargs)
class GeoNodeManager(RetrieveMixin, UpdateMixin, DeleteMixin, RESTManager):
- _path = '/geo_nodes'
+ _path = "/geo_nodes"
_obj_cls = GeoNode
- _update_attrs = (tuple(), ('enabled', 'url', 'files_max_capacity',
- 'repos_max_capacity'))
+ _update_attrs = (
+ tuple(),
+ ("enabled", "url", "files_max_capacity", "repos_max_capacity"),
+ )
- @cli.register_custom_action('GeoNodeManager')
+ @cli.register_custom_action("GeoNodeManager")
@exc.on_http_error(exc.GitlabGetError)
def status(self, **kwargs):
"""Get the status of all the geo nodes.
@@ -4077,9 +4527,9 @@ class GeoNodeManager(RetrieveMixin, UpdateMixin, DeleteMixin, RESTManager):
Returns:
list: The status of all the geo nodes
"""
- return self.gitlab.http_list('/geo_nodes/status', **kwargs)
+ return self.gitlab.http_list("/geo_nodes/status", **kwargs)
- @cli.register_custom_action('GeoNodeManager')
+ @cli.register_custom_action("GeoNodeManager")
@exc.on_http_error(exc.GitlabGetError)
def current_failures(self, **kwargs):
"""Get the list of failures on the current geo node.
@@ -4094,4 +4544,4 @@ class GeoNodeManager(RetrieveMixin, UpdateMixin, DeleteMixin, RESTManager):
Returns:
list: The list of failures
"""
- return self.gitlab.http_list('/geo_nodes/current/failures', **kwargs)
+ return self.gitlab.http_list("/geo_nodes/current/failures", **kwargs)
diff --git a/setup.py b/setup.py
index b592e7c..dff0830 100644
--- a/setup.py
+++ b/setup.py
@@ -6,43 +6,41 @@ from setuptools import find_packages
def get_version():
- with open('gitlab/__init__.py') as f:
+ with open("gitlab/__init__.py") as f:
for line in f:
- if line.startswith('__version__'):
- return eval(line.split('=')[-1])
+ if line.startswith("__version__"):
+ return eval(line.split("=")[-1])
+
with open("README.rst", "r") as readme_file:
readme = readme_file.read()
-setup(name='python-gitlab',
- version=get_version(),
- description='Interact with GitLab API',
- long_description=readme,
- author='Gauvain Pocentek',
- author_email='gauvain@pocentek.net',
- license='LGPLv3',
- url='https://github.com/python-gitlab/python-gitlab',
- packages=find_packages(),
- install_requires=['requests>=2.4.2', 'six'],
- entry_points={
- 'console_scripts': [
- 'gitlab = gitlab.cli:main'
- ]
- },
- classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Console',
- 'Intended Audience :: System Administrators',
- 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',
- 'Natural Language :: English',
- 'Operating System :: POSIX',
- 'Operating System :: Microsoft :: Windows',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- ]
- )
+setup(
+ name="python-gitlab",
+ version=get_version(),
+ description="Interact with GitLab API",
+ long_description=readme,
+ author="Gauvain Pocentek",
+ author_email="gauvain@pocentek.net",
+ license="LGPLv3",
+ url="https://github.com/python-gitlab/python-gitlab",
+ packages=find_packages(),
+ install_requires=["requests>=2.4.2", "six"],
+ entry_points={"console_scripts": ["gitlab = gitlab.cli:main"]},
+ classifiers=[
+ "Development Status :: 5 - Production/Stable",
+ "Environment :: Console",
+ "Intended Audience :: System Administrators",
+ "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
+ "Natural Language :: English",
+ "Operating System :: POSIX",
+ "Operating System :: Microsoft :: Windows",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ ],
+)
diff --git a/tools/ee-test.py b/tools/ee-test.py
index bc98cc6..24a9b35 100755
--- a/tools/ee-test.py
+++ b/tools/ee-test.py
@@ -3,26 +3,26 @@
import gitlab
-P1 = 'root/project1'
-P2 = 'root/project2'
+P1 = "root/project1"
+P2 = "root/project2"
MR_P1 = 1
I_P1 = 1
I_P2 = 1
EPIC_ISSUES = [4, 5]
-G1 = 'group1'
-LDAP_CN = 'app1'
-LDAP_PROVIDER = 'ldapmain'
+G1 = "group1"
+LDAP_CN = "app1"
+LDAP_PROVIDER = "ldapmain"
def start_log(message):
- print('Testing %s... ' % message, end='')
+ print("Testing %s... " % message, end="")
def end_log():
- print('OK')
+ print("OK")
-gl = gitlab.Gitlab.from_config('ee')
+gl = gitlab.Gitlab.from_config("ee")
project1 = gl.projects.get(P1)
project2 = gl.projects.get(P2)
issue_p1 = project1.issues.get(I_P1)
@@ -30,113 +30,112 @@ issue_p2 = project2.issues.get(I_P2)
group1 = gl.groups.get(G1)
mr = project1.mergerequests.get(1)
-start_log('MR approvals')
+start_log("MR approvals")
approval = project1.approvals.get()
v = approval.reset_approvals_on_push
approval.reset_approvals_on_push = not v
approval.save()
approval = project1.approvals.get()
-assert(v != approval.reset_approvals_on_push)
+assert v != approval.reset_approvals_on_push
project1.approvals.set_approvers([1], [])
approval = project1.approvals.get()
-assert(approval.approvers[0]['user']['id'] == 1)
+assert approval.approvers[0]["user"]["id"] == 1
approval = mr.approvals.get()
approval.approvals_required = 2
approval.save()
approval = mr.approvals.get()
-assert(approval.approvals_required == 2)
+assert approval.approvals_required == 2
approval.approvals_required = 3
approval.save()
approval = mr.approvals.get()
-assert(approval.approvals_required == 3)
+assert approval.approvals_required == 3
mr.approvals.set_approvers([1], [])
approval = mr.approvals.get()
-assert(approval.approvers[0]['user']['id'] == 1)
+assert approval.approvers[0]["user"]["id"] == 1
end_log()
-start_log('geo nodes')
+start_log("geo nodes")
# very basic tests because we only have 1 node...
nodes = gl.geonodes.list()
status = gl.geonodes.status()
end_log()
-start_log('issue links')
+start_log("issue links")
# bit of cleanup just in case
for link in issue_p1.links.list():
issue_p1.links.delete(link.issue_link_id)
-src, dst = issue_p1.links.create({'target_project_id': P2,
- 'target_issue_iid': I_P2})
+src, dst = issue_p1.links.create({"target_project_id": P2, "target_issue_iid": I_P2})
links = issue_p1.links.list()
link_id = links[0].issue_link_id
issue_p1.links.delete(link_id)
end_log()
-start_log('LDAP links')
+start_log("LDAP links")
# bit of cleanup just in case
-if hasattr(group1, 'ldap_group_links'):
+if hasattr(group1, "ldap_group_links"):
for link in group1.ldap_group_links:
- group1.delete_ldap_group_link(link['cn'], link['provider'])
-assert(gl.ldapgroups.list())
+ group1.delete_ldap_group_link(link["cn"], link["provider"])
+assert gl.ldapgroups.list()
group1.add_ldap_group_link(LDAP_CN, 30, LDAP_PROVIDER)
group1.ldap_sync()
group1.delete_ldap_group_link(LDAP_CN)
end_log()
-start_log('boards')
+start_log("boards")
# bit of cleanup just in case
for board in project1.boards.list():
- if board.name == 'testboard':
+ if board.name == "testboard":
board.delete()
-board = project1.boards.create({'name': 'testboard'})
+board = project1.boards.create({"name": "testboard"})
board = project1.boards.get(board.id)
project1.boards.delete(board.id)
for board in group1.boards.list():
- if board.name == 'testboard':
+ if board.name == "testboard":
board.delete()
-board = group1.boards.create({'name': 'testboard'})
+board = group1.boards.create({"name": "testboard"})
board = group1.boards.get(board.id)
group1.boards.delete(board.id)
end_log()
-start_log('push rules')
+start_log("push rules")
pr = project1.pushrules.get()
if pr:
pr.delete()
-pr = project1.pushrules.create({'deny_delete_tag': True})
+pr = project1.pushrules.create({"deny_delete_tag": True})
pr.deny_delete_tag = False
pr.save()
pr = project1.pushrules.get()
-assert(pr is not None)
-assert(pr.deny_delete_tag == False)
+assert pr is not None
+assert pr.deny_delete_tag == False
pr.delete()
end_log()
-start_log('license')
+start_log("license")
l = gl.get_license()
-assert('user_limit' in l)
+assert "user_limit" in l
try:
- gl.set_license('dummykey')
+ gl.set_license("dummykey")
except Exception as e:
- assert('The license key is invalid.' in e.error_message)
+ assert "The license key is invalid." in e.error_message
end_log()
-start_log('epics')
-epic = group1.epics.create({'title': 'Test epic'})
-epic.title = 'Fixed title'
-epic.labels = ['label1', 'label2']
+start_log("epics")
+epic = group1.epics.create({"title": "Test epic"})
+epic.title = "Fixed title"
+epic.labels = ["label1", "label2"]
epic.save()
epic = group1.epics.get(epic.iid)
-assert(epic.title == 'Fixed title')
-assert(len(group1.epics.list()))
+assert epic.title == "Fixed title"
+assert len(group1.epics.list())
# issues
-assert(not epic.issues.list())
+assert not epic.issues.list()
for i in EPIC_ISSUES:
- epic.issues.create({'issue_id': i})
-assert(len(EPIC_ISSUES) == len(epic.issues.list()))
+ epic.issues.create({"issue_id": i})
+assert len(EPIC_ISSUES) == len(epic.issues.list())
for ei in epic.issues.list():
ei.delete()
diff --git a/tools/generate_token.py b/tools/generate_token.py
index ab14188..9fa2ff2 100755
--- a/tools/generate_token.py
+++ b/tools/generate_token.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
import sys
+
try:
from urllib.parse import urljoin
except ImportError:
@@ -33,10 +34,7 @@ def obtain_csrf_token():
def sign_in(csrf, cookies):
- data = {
- "user[login]": login,
- "user[password]": password,
- }
+ data = {"user[login]": login, "user[password]": password}
data.update(csrf)
r = requests.post(sign_in_route, data=data, cookies=cookies)
token = find_csrf_token(r.text)
@@ -51,7 +49,7 @@ def obtain_personal_access_token(name, csrf, cookies):
data.update(csrf)
r = requests.post(pat_route, data=data, cookies=cookies)
soup = BeautifulSoup(r.text, "lxml")
- token = soup.find('input', id='created-personal-access-token').get('value')
+ token = soup.find("input", id="created-personal-access-token").get("value")
return token
@@ -59,7 +57,7 @@ def main():
csrf1, cookies1 = obtain_csrf_token()
csrf2, cookies2 = sign_in(csrf1, cookies1)
- token = obtain_personal_access_token('default', csrf2, cookies2)
+ token = obtain_personal_access_token("default", csrf2, cookies2)
print(token)
diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py
index 958e350..a00ae29 100644
--- a/tools/python_test_v4.py
+++ b/tools/python_test_v4.py
@@ -6,24 +6,28 @@ import requests
import gitlab
-LOGIN = 'root'
-PASSWORD = '5iveL!fe'
-
-SSH_KEY = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZAjAX8vTiHD7Yi3/EzuVaDChtih"
- "79HyJZ6H9dEqxFfmGA1YnncE0xujQ64TCebhkYJKzmTJCImSVkOu9C4hZgsw6eE76n"
- "+Cg3VwEeDUFy+GXlEJWlHaEyc3HWioxgOALbUp3rOezNh+d8BDwwqvENGoePEBsz5l"
- "a6WP5lTi/HJIjAl6Hu+zHgdj1XVExeH+S52EwpZf/ylTJub0Bl5gHwf/siVE48mLMI"
- "sqrukXTZ6Zg+8EHAIvIQwJ1dKcXe8P5IoLT7VKrbkgAnolS0I8J+uH7KtErZJb5oZh"
- "S4OEwsNpaXMAr+6/wWSpircV2/e7sFLlhlKBC4Iq1MpqlZ7G3p foo@bar")
-DEPLOY_KEY = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFdRyjJQh+1niBpXqE2I8dzjG"
- "MXFHlRjX9yk/UfOn075IdaockdU58sw2Ai1XIWFpZpfJkW7z+P47ZNSqm1gzeXI"
- "rtKa9ZUp8A7SZe8vH4XVn7kh7bwWCUirqtn8El9XdqfkzOs/+FuViriUWoJVpA6"
- "WZsDNaqINFKIA5fj/q8XQw+BcS92L09QJg9oVUuH0VVwNYbU2M2IRmSpybgC/gu"
- "uWTrnCDMmLItksATifLvRZwgdI8dr+q6tbxbZknNcgEPrI2jT0hYN9ZcjNeWuyv"
- "rke9IepE7SPBT41C+YtUX4dfDZDmczM1cE0YL/krdUCfuZHMa4ZS2YyNd6slufc"
- "vn bar@foo")
-
-GPG_KEY = '''-----BEGIN PGP PUBLIC KEY BLOCK-----
+LOGIN = "root"
+PASSWORD = "5iveL!fe"
+
+SSH_KEY = (
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZAjAX8vTiHD7Yi3/EzuVaDChtih"
+ "79HyJZ6H9dEqxFfmGA1YnncE0xujQ64TCebhkYJKzmTJCImSVkOu9C4hZgsw6eE76n"
+ "+Cg3VwEeDUFy+GXlEJWlHaEyc3HWioxgOALbUp3rOezNh+d8BDwwqvENGoePEBsz5l"
+ "a6WP5lTi/HJIjAl6Hu+zHgdj1XVExeH+S52EwpZf/ylTJub0Bl5gHwf/siVE48mLMI"
+ "sqrukXTZ6Zg+8EHAIvIQwJ1dKcXe8P5IoLT7VKrbkgAnolS0I8J+uH7KtErZJb5oZh"
+ "S4OEwsNpaXMAr+6/wWSpircV2/e7sFLlhlKBC4Iq1MpqlZ7G3p foo@bar"
+)
+DEPLOY_KEY = (
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFdRyjJQh+1niBpXqE2I8dzjG"
+ "MXFHlRjX9yk/UfOn075IdaockdU58sw2Ai1XIWFpZpfJkW7z+P47ZNSqm1gzeXI"
+ "rtKa9ZUp8A7SZe8vH4XVn7kh7bwWCUirqtn8El9XdqfkzOs/+FuViriUWoJVpA6"
+ "WZsDNaqINFKIA5fj/q8XQw+BcS92L09QJg9oVUuH0VVwNYbU2M2IRmSpybgC/gu"
+ "uWTrnCDMmLItksATifLvRZwgdI8dr+q6tbxbZknNcgEPrI2jT0hYN9ZcjNeWuyv"
+ "rke9IepE7SPBT41C+YtUX4dfDZDmczM1cE0YL/krdUCfuZHMa4ZS2YyNd6slufc"
+ "vn bar@foo"
+)
+
+GPG_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFn5mzYBCADH6SDVPAp1zh/hxmTi0QplkOfExBACpuY6OhzNdIg+8/528b3g
Y5YFR6T/HLv/PmeHskUj21end1C0PNG2T9dTx+2Vlh9ISsSG1kyF9T5fvMR3bE0x
@@ -51,608 +55,636 @@ YzGsC/I9d7k6uxAv1L9Nm5F2HaAQDzhkdd16nKkGaPGR35cT1JLInkfl5cdm7ldN
nxs4TLO3kZjUTgWKdhpgRNF5hwaz51ZjpebaRf/ZqRuNyX4lIRolDxzOn/+O1o8L
qG2ZdhHHmSK2LaQLFiSprUkikStNU9BqSQ==
=5OGa
------END PGP PUBLIC KEY BLOCK-----'''
-AVATAR_PATH = os.path.join(os.path.dirname(__file__), 'avatar.png')
+-----END PGP PUBLIC KEY BLOCK-----"""
+AVATAR_PATH = os.path.join(os.path.dirname(__file__), "avatar.png")
# token authentication from config file
-gl = gitlab.Gitlab.from_config(config_files=['/tmp/python-gitlab.cfg'])
+gl = gitlab.Gitlab.from_config(config_files=["/tmp/python-gitlab.cfg"])
gl.auth()
-assert(isinstance(gl.user, gitlab.v4.objects.CurrentUser))
+assert isinstance(gl.user, gitlab.v4.objects.CurrentUser)
# markdown (need to wait for gitlab 11 to enable the test)
# html = gl.markdown('foo')
# assert('foo' in html)
-success, errors = gl.lint('Invalid')
-assert(success is False)
-assert(errors)
+success, errors = gl.lint("Invalid")
+assert success is False
+assert errors
# sidekiq
out = gl.sidekiq.queue_metrics()
-assert(isinstance(out, dict))
-assert('pages' in out['queues'])
+assert isinstance(out, dict)
+assert "pages" in out["queues"]
out = gl.sidekiq.process_metrics()
-assert(isinstance(out, dict))
-assert('hostname' in out['processes'][0])
+assert isinstance(out, dict)
+assert "hostname" in out["processes"][0]
out = gl.sidekiq.job_stats()
-assert(isinstance(out, dict))
-assert('processed' in out['jobs'])
+assert isinstance(out, dict)
+assert "processed" in out["jobs"]
out = gl.sidekiq.compound_metrics()
-assert(isinstance(out, dict))
-assert('jobs' in out)
-assert('processes' in out)
-assert('queues' in out)
+assert isinstance(out, dict)
+assert "jobs" in out
+assert "processes" in out
+assert "queues" in out
# settings
settings = gl.settings.get()
settings.default_projects_limit = 42
settings.save()
settings = gl.settings.get()
-assert(settings.default_projects_limit == 42)
+assert settings.default_projects_limit == 42
# users
-new_user = gl.users.create({'email': 'foo@bar.com', 'username': 'foo',
- 'name': 'foo', 'password': 'foo_password',
- 'avatar': open(AVATAR_PATH, 'rb')})
-avatar_url = new_user.avatar_url.replace('gitlab.test', 'localhost:8080')
+new_user = gl.users.create(
+ {
+ "email": "foo@bar.com",
+ "username": "foo",
+ "name": "foo",
+ "password": "foo_password",
+ "avatar": open(AVATAR_PATH, "rb"),
+ }
+)
+avatar_url = new_user.avatar_url.replace("gitlab.test", "localhost:8080")
uploaded_avatar = requests.get(avatar_url).content
-assert(uploaded_avatar == open(AVATAR_PATH, 'rb').read())
+assert uploaded_avatar == open(AVATAR_PATH, "rb").read()
users_list = gl.users.list()
for user in users_list:
- if user.username == 'foo':
+ if user.username == "foo":
break
-assert(new_user.username == user.username)
-assert(new_user.email == user.email)
+assert new_user.username == user.username
+assert new_user.email == user.email
new_user.block()
new_user.unblock()
# user projects list
-assert(len(new_user.projects.list()) == 0)
+assert len(new_user.projects.list()) == 0
# events list
new_user.events.list()
foobar_user = gl.users.create(
- {'email': 'foobar@example.com', 'username': 'foobar',
- 'name': 'Foo Bar', 'password': 'foobar_password'})
+ {
+ "email": "foobar@example.com",
+ "username": "foobar",
+ "name": "Foo Bar",
+ "password": "foobar_password",
+ }
+)
-assert gl.users.list(search='foobar')[0].id == foobar_user.id
+assert gl.users.list(search="foobar")[0].id == foobar_user.id
expected = [new_user, foobar_user]
-actual = list(gl.users.list(search='foo'))
+actual = list(gl.users.list(search="foo"))
assert len(expected) == len(actual)
-assert len(gl.users.list(search='asdf')) == 0
-foobar_user.bio = 'This is the user bio'
+assert len(gl.users.list(search="asdf")) == 0
+foobar_user.bio = "This is the user bio"
foobar_user.save()
# GPG keys
-gkey = new_user.gpgkeys.create({'key': GPG_KEY})
-assert(len(new_user.gpgkeys.list()) == 1)
+gkey = new_user.gpgkeys.create({"key": GPG_KEY})
+assert len(new_user.gpgkeys.list()) == 1
# Seems broken on the gitlab side
# gkey = new_user.gpgkeys.get(gkey.id)
gkey.delete()
-assert(len(new_user.gpgkeys.list()) == 0)
+assert len(new_user.gpgkeys.list()) == 0
# SSH keys
-key = new_user.keys.create({'title': 'testkey', 'key': SSH_KEY})
-assert(len(new_user.keys.list()) == 1)
+key = new_user.keys.create({"title": "testkey", "key": SSH_KEY})
+assert len(new_user.keys.list()) == 1
key.delete()
-assert(len(new_user.keys.list()) == 0)
+assert len(new_user.keys.list()) == 0
# emails
-email = new_user.emails.create({'email': 'foo2@bar.com'})
-assert(len(new_user.emails.list()) == 1)
+email = new_user.emails.create({"email": "foo2@bar.com"})
+assert len(new_user.emails.list()) == 1
email.delete()
-assert(len(new_user.emails.list()) == 0)
+assert len(new_user.emails.list()) == 0
# custom attributes
attrs = new_user.customattributes.list()
-assert(len(attrs) == 0)
-attr = new_user.customattributes.set('key', 'value1')
-assert(len(gl.users.list(custom_attributes={'key': 'value1'})) == 1)
-assert(attr.key == 'key')
-assert(attr.value == 'value1')
-assert(len(new_user.customattributes.list()) == 1)
-attr = new_user.customattributes.set('key', 'value2')
-attr = new_user.customattributes.get('key')
-assert(attr.value == 'value2')
-assert(len(new_user.customattributes.list()) == 1)
+assert len(attrs) == 0
+attr = new_user.customattributes.set("key", "value1")
+assert len(gl.users.list(custom_attributes={"key": "value1"})) == 1
+assert attr.key == "key"
+assert attr.value == "value1"
+assert len(new_user.customattributes.list()) == 1
+attr = new_user.customattributes.set("key", "value2")
+attr = new_user.customattributes.get("key")
+assert attr.value == "value2"
+assert len(new_user.customattributes.list()) == 1
attr.delete()
-assert(len(new_user.customattributes.list()) == 0)
+assert len(new_user.customattributes.list()) == 0
# impersonation tokens
user_token = new_user.impersonationtokens.create(
- {'name': 'token1', 'scopes': ['api', 'read_user']})
-l = new_user.impersonationtokens.list(state='active')
-assert(len(l) == 1)
+ {"name": "token1", "scopes": ["api", "read_user"]}
+)
+l = new_user.impersonationtokens.list(state="active")
+assert len(l) == 1
user_token.delete()
-l = new_user.impersonationtokens.list(state='active')
-assert(len(l) == 0)
-l = new_user.impersonationtokens.list(state='inactive')
-assert(len(l) == 1)
+l = new_user.impersonationtokens.list(state="active")
+assert len(l) == 0
+l = new_user.impersonationtokens.list(state="inactive")
+assert len(l) == 1
new_user.delete()
foobar_user.delete()
-assert(len(gl.users.list()) == 3)
+assert len(gl.users.list()) == 3
# current user mail
-mail = gl.user.emails.create({'email': 'current@user.com'})
-assert(len(gl.user.emails.list()) == 1)
+mail = gl.user.emails.create({"email": "current@user.com"})
+assert len(gl.user.emails.list()) == 1
mail.delete()
-assert(len(gl.user.emails.list()) == 0)
+assert len(gl.user.emails.list()) == 0
# current user GPG keys
-gkey = gl.user.gpgkeys.create({'key': GPG_KEY})
-assert(len(gl.user.gpgkeys.list()) == 1)
+gkey = gl.user.gpgkeys.create({"key": GPG_KEY})
+assert len(gl.user.gpgkeys.list()) == 1
# Seems broken on the gitlab side
gkey = gl.user.gpgkeys.get(gkey.id)
gkey.delete()
-assert(len(gl.user.gpgkeys.list()) == 0)
+assert len(gl.user.gpgkeys.list()) == 0
# current user key
-key = gl.user.keys.create({'title': 'testkey', 'key': SSH_KEY})
-assert(len(gl.user.keys.list()) == 1)
+key = gl.user.keys.create({"title": "testkey", "key": SSH_KEY})
+assert len(gl.user.keys.list()) == 1
key.delete()
-assert(len(gl.user.keys.list()) == 0)
+assert len(gl.user.keys.list()) == 0
# templates
-assert(gl.dockerfiles.list())
-dockerfile = gl.dockerfiles.get('Node')
-assert(dockerfile.content is not None)
+assert gl.dockerfiles.list()
+dockerfile = gl.dockerfiles.get("Node")
+assert dockerfile.content is not None
-assert(gl.gitignores.list())
-gitignore = gl.gitignores.get('Node')
-assert(gitignore.content is not None)
+assert gl.gitignores.list()
+gitignore = gl.gitignores.get("Node")
+assert gitignore.content is not None
-assert(gl.gitlabciymls.list())
-gitlabciyml = gl.gitlabciymls.get('Nodejs')
-assert(gitlabciyml.content is not None)
+assert gl.gitlabciymls.list()
+gitlabciyml = gl.gitlabciymls.get("Nodejs")
+assert gitlabciyml.content is not None
-assert(gl.licenses.list())
-license = gl.licenses.get('bsd-2-clause', project='mytestproject',
- fullname='mytestfullname')
-assert('mytestfullname' in license.content)
+assert gl.licenses.list()
+license = gl.licenses.get(
+ "bsd-2-clause", project="mytestproject", fullname="mytestfullname"
+)
+assert "mytestfullname" in license.content
# groups
-user1 = gl.users.create({'email': 'user1@test.com', 'username': 'user1',
- 'name': 'user1', 'password': 'user1_pass'})
-user2 = gl.users.create({'email': 'user2@test.com', 'username': 'user2',
- 'name': 'user2', 'password': 'user2_pass'})
-group1 = gl.groups.create({'name': 'group1', 'path': 'group1'})
-group2 = gl.groups.create({'name': 'group2', 'path': 'group2'})
+user1 = gl.users.create(
+ {
+ "email": "user1@test.com",
+ "username": "user1",
+ "name": "user1",
+ "password": "user1_pass",
+ }
+)
+user2 = gl.users.create(
+ {
+ "email": "user2@test.com",
+ "username": "user2",
+ "name": "user2",
+ "password": "user2_pass",
+ }
+)
+group1 = gl.groups.create({"name": "group1", "path": "group1"})
+group2 = gl.groups.create({"name": "group2", "path": "group2"})
-p_id = gl.groups.list(search='group2')[0].id
-group3 = gl.groups.create({'name': 'group3', 'path': 'group3', 'parent_id': p_id})
+p_id = gl.groups.list(search="group2")[0].id
+group3 = gl.groups.create({"name": "group3", "path": "group3", "parent_id": p_id})
-assert(len(gl.groups.list()) == 3)
-assert(len(gl.groups.list(search='oup1')) == 1)
-assert(group3.parent_id == p_id)
-assert(group2.subgroups.list()[0].id == group3.id)
+assert len(gl.groups.list()) == 3
+assert len(gl.groups.list(search="oup1")) == 1
+assert group3.parent_id == p_id
+assert group2.subgroups.list()[0].id == group3.id
-group1.members.create({'access_level': gitlab.const.OWNER_ACCESS,
- 'user_id': user1.id})
-group1.members.create({'access_level': gitlab.const.GUEST_ACCESS,
- 'user_id': user2.id})
+group1.members.create({"access_level": gitlab.const.OWNER_ACCESS, "user_id": user1.id})
+group1.members.create({"access_level": gitlab.const.GUEST_ACCESS, "user_id": user2.id})
-group2.members.create({'access_level': gitlab.const.OWNER_ACCESS,
- 'user_id': user2.id})
+group2.members.create({"access_level": gitlab.const.OWNER_ACCESS, "user_id": user2.id})
# Administrator belongs to the groups
-assert(len(group1.members.list()) == 3)
-assert(len(group2.members.list()) == 2)
+assert len(group1.members.list()) == 3
+assert len(group2.members.list()) == 2
group1.members.delete(user1.id)
-assert(len(group1.members.list()) == 2)
-assert(len(group1.members.all()))
+assert len(group1.members.list()) == 2
+assert len(group1.members.all())
member = group1.members.get(user2.id)
member.access_level = gitlab.const.OWNER_ACCESS
member.save()
member = group1.members.get(user2.id)
-assert(member.access_level == gitlab.const.OWNER_ACCESS)
+assert member.access_level == gitlab.const.OWNER_ACCESS
group2.members.delete(gl.user.id)
# group custom attributes
attrs = group2.customattributes.list()
-assert(len(attrs) == 0)
-attr = group2.customattributes.set('key', 'value1')
-assert(len(gl.groups.list(custom_attributes={'key': 'value1'})) == 1)
-assert(attr.key == 'key')
-assert(attr.value == 'value1')
-assert(len(group2.customattributes.list()) == 1)
-attr = group2.customattributes.set('key', 'value2')
-attr = group2.customattributes.get('key')
-assert(attr.value == 'value2')
-assert(len(group2.customattributes.list()) == 1)
+assert len(attrs) == 0
+attr = group2.customattributes.set("key", "value1")
+assert len(gl.groups.list(custom_attributes={"key": "value1"})) == 1
+assert attr.key == "key"
+assert attr.value == "value1"
+assert len(group2.customattributes.list()) == 1
+attr = group2.customattributes.set("key", "value2")
+attr = group2.customattributes.get("key")
+assert attr.value == "value2"
+assert len(group2.customattributes.list()) == 1
attr.delete()
-assert(len(group2.customattributes.list()) == 0)
+assert len(group2.customattributes.list()) == 0
# group notification settings
settings = group2.notificationsettings.get()
-settings.level = 'disabled'
+settings.level = "disabled"
settings.save()
settings = group2.notificationsettings.get()
-assert(settings.level == 'disabled')
+assert settings.level == "disabled"
# group badges
-badge_image = 'http://example.com'
-badge_link = 'http://example/img.svg'
-badge = group2.badges.create({'link_url': badge_link, 'image_url': badge_image})
-assert(len(group2.badges.list()) == 1)
-badge.image_url = 'http://another.example.com'
+badge_image = "http://example.com"
+badge_link = "http://example/img.svg"
+badge = group2.badges.create({"link_url": badge_link, "image_url": badge_image})
+assert len(group2.badges.list()) == 1
+badge.image_url = "http://another.example.com"
badge.save()
badge = group2.badges.get(badge.id)
-assert(badge.image_url == 'http://another.example.com')
+assert badge.image_url == "http://another.example.com"
badge.delete()
-assert(len(group2.badges.list()) == 0)
+assert len(group2.badges.list()) == 0
# group milestones
-gm1 = group1.milestones.create({'title': 'groupmilestone1'})
-assert(len(group1.milestones.list()) == 1)
-gm1.due_date = '2020-01-01T00:00:00Z'
+gm1 = group1.milestones.create({"title": "groupmilestone1"})
+assert len(group1.milestones.list()) == 1
+gm1.due_date = "2020-01-01T00:00:00Z"
gm1.save()
-gm1.state_event = 'close'
+gm1.state_event = "close"
gm1.save()
gm1 = group1.milestones.get(gm1.id)
-assert(gm1.state == 'closed')
-assert(len(gm1.issues()) == 0)
-assert(len(gm1.merge_requests()) == 0)
+assert gm1.state == "closed"
+assert len(gm1.issues()) == 0
+assert len(gm1.merge_requests()) == 0
# group variables
-group1.variables.create({'key': 'foo', 'value': 'bar'})
-g_v = group1.variables.get('foo')
-assert(g_v.value == 'bar')
-g_v.value = 'baz'
+group1.variables.create({"key": "foo", "value": "bar"})
+g_v = group1.variables.get("foo")
+assert g_v.value == "bar"
+g_v.value = "baz"
g_v.save()
-g_v = group1.variables.get('foo')
-assert(g_v.value == 'baz')
-assert(len(group1.variables.list()) == 1)
+g_v = group1.variables.get("foo")
+assert g_v.value == "baz"
+assert len(group1.variables.list()) == 1
g_v.delete()
-assert(len(group1.variables.list()) == 0)
+assert len(group1.variables.list()) == 0
# hooks
-hook = gl.hooks.create({'url': 'http://whatever.com'})
-assert(len(gl.hooks.list()) == 1)
+hook = gl.hooks.create({"url": "http://whatever.com"})
+assert len(gl.hooks.list()) == 1
hook.delete()
-assert(len(gl.hooks.list()) == 0)
+assert len(gl.hooks.list()) == 0
# projects
-admin_project = gl.projects.create({'name': 'admin_project'})
-gr1_project = gl.projects.create({'name': 'gr1_project',
- 'namespace_id': group1.id})
-gr2_project = gl.projects.create({'name': 'gr2_project',
- 'namespace_id': group2.id})
-sudo_project = gl.projects.create({'name': 'sudo_project'}, sudo=user1.name)
+admin_project = gl.projects.create({"name": "admin_project"})
+gr1_project = gl.projects.create({"name": "gr1_project", "namespace_id": group1.id})
+gr2_project = gl.projects.create({"name": "gr2_project", "namespace_id": group2.id})
+sudo_project = gl.projects.create({"name": "sudo_project"}, sudo=user1.name)
-assert(len(gl.projects.list(owned=True)) == 2)
-assert(len(gl.projects.list(search="admin")) == 1)
+assert len(gl.projects.list(owned=True)) == 2
+assert len(gl.projects.list(search="admin")) == 1
# test pagination
l1 = gl.projects.list(per_page=1, page=1)
l2 = gl.projects.list(per_page=1, page=2)
-assert(len(l1) == 1)
-assert(len(l2) == 1)
-assert(l1[0].id != l2[0].id)
+assert len(l1) == 1
+assert len(l2) == 1
+assert l1[0].id != l2[0].id
# group custom attributes
attrs = admin_project.customattributes.list()
-assert(len(attrs) == 0)
-attr = admin_project.customattributes.set('key', 'value1')
-assert(len(gl.projects.list(custom_attributes={'key': 'value1'})) == 1)
-assert(attr.key == 'key')
-assert(attr.value == 'value1')
-assert(len(admin_project.customattributes.list()) == 1)
-attr = admin_project.customattributes.set('key', 'value2')
-attr = admin_project.customattributes.get('key')
-assert(attr.value == 'value2')
-assert(len(admin_project.customattributes.list()) == 1)
+assert len(attrs) == 0
+attr = admin_project.customattributes.set("key", "value1")
+assert len(gl.projects.list(custom_attributes={"key": "value1"})) == 1
+assert attr.key == "key"
+assert attr.value == "value1"
+assert len(admin_project.customattributes.list()) == 1
+attr = admin_project.customattributes.set("key", "value2")
+attr = admin_project.customattributes.get("key")
+assert attr.value == "value2"
+assert len(admin_project.customattributes.list()) == 1
attr.delete()
-assert(len(admin_project.customattributes.list()) == 0)
+assert len(admin_project.customattributes.list()) == 0
# project pages domains
-domain = admin_project.pagesdomains.create({'domain': 'foo.domain.com'})
-assert(len(admin_project.pagesdomains.list()) == 1)
-assert(len(gl.pagesdomains.list()) == 1)
-domain = admin_project.pagesdomains.get('foo.domain.com')
-assert(domain.domain == 'foo.domain.com')
+domain = admin_project.pagesdomains.create({"domain": "foo.domain.com"})
+assert len(admin_project.pagesdomains.list()) == 1
+assert len(gl.pagesdomains.list()) == 1
+domain = admin_project.pagesdomains.get("foo.domain.com")
+assert domain.domain == "foo.domain.com"
domain.delete()
-assert(len(admin_project.pagesdomains.list()) == 0)
+assert len(admin_project.pagesdomains.list()) == 0
# project content (files)
-admin_project.files.create({'file_path': 'README',
- 'branch': 'master',
- 'content': 'Initial content',
- 'commit_message': 'Initial commit'})
-readme = admin_project.files.get(file_path='README', ref='master')
+admin_project.files.create(
+ {
+ "file_path": "README",
+ "branch": "master",
+ "content": "Initial content",
+ "commit_message": "Initial commit",
+ }
+)
+readme = admin_project.files.get(file_path="README", ref="master")
readme.content = base64.b64encode(b"Improved README").decode()
time.sleep(2)
readme.save(branch="master", commit_message="new commit")
readme.delete(commit_message="Removing README", branch="master")
-admin_project.files.create({'file_path': 'README.rst',
- 'branch': 'master',
- 'content': 'Initial content',
- 'commit_message': 'New commit'})
-readme = admin_project.files.get(file_path='README.rst', ref='master')
+admin_project.files.create(
+ {
+ "file_path": "README.rst",
+ "branch": "master",
+ "content": "Initial content",
+ "commit_message": "New commit",
+ }
+)
+readme = admin_project.files.get(file_path="README.rst", ref="master")
# The first decode() is the ProjectFile method, the second one is the bytes
# object method
-assert(readme.decode().decode() == 'Initial content')
+assert readme.decode().decode() == "Initial content"
data = {
- 'branch': 'master',
- 'commit_message': 'blah blah blah',
- 'actions': [
- {
- 'action': 'create',
- 'file_path': 'blah',
- 'content': 'blah'
- }
- ]
+ "branch": "master",
+ "commit_message": "blah blah blah",
+ "actions": [{"action": "create", "file_path": "blah", "content": "blah"}],
}
admin_project.commits.create(data)
-assert('@@' in admin_project.commits.list()[0].diff()[0]['diff'])
+assert "@@" in admin_project.commits.list()[0].diff()[0]["diff"]
# commit status
commit = admin_project.commits.list()[0]
-status = commit.statuses.create({'state': 'success', 'sha': commit.id})
-assert(len(commit.statuses.list()) == 1)
+status = commit.statuses.create({"state": "success", "sha": commit.id})
+assert len(commit.statuses.list()) == 1
-assert(commit.refs())
-assert(commit.merge_requests() is not None)
+assert commit.refs()
+assert commit.merge_requests() is not None
# commit comment
-commit.comments.create({'note': 'This is a commit comment'})
-assert(len(commit.comments.list()) == 1)
+commit.comments.create({"note": "This is a commit comment"})
+assert len(commit.comments.list()) == 1
# commit discussion
count = len(commit.discussions.list())
-discussion = commit.discussions.create({'body': 'Discussion body'})
-assert(len(commit.discussions.list()) == (count + 1))
-d_note = discussion.notes.create({'body': 'first note'})
+discussion = commit.discussions.create({"body": "Discussion body"})
+assert len(commit.discussions.list()) == (count + 1)
+d_note = discussion.notes.create({"body": "first note"})
d_note_from_get = discussion.notes.get(d_note.id)
-d_note_from_get.body = 'updated body'
+d_note_from_get.body = "updated body"
d_note_from_get.save()
discussion = commit.discussions.get(discussion.id)
-assert(discussion.attributes['notes'][-1]['body'] == 'updated body')
+assert discussion.attributes["notes"][-1]["body"] == "updated body"
d_note_from_get.delete()
discussion = commit.discussions.get(discussion.id)
-assert(len(discussion.attributes['notes']) == 1)
+assert len(discussion.attributes["notes"]) == 1
# housekeeping
admin_project.housekeeping()
# repository
tree = admin_project.repository_tree()
-assert(len(tree) != 0)
-assert(tree[0]['name'] == 'README.rst')
-blob_id = tree[0]['id']
+assert len(tree) != 0
+assert tree[0]["name"] == "README.rst"
+blob_id = tree[0]["id"]
blob = admin_project.repository_raw_blob(blob_id)
-assert(blob.decode() == 'Initial content')
+assert blob.decode() == "Initial content"
archive1 = admin_project.repository_archive()
-archive2 = admin_project.repository_archive('master')
-assert(archive1 == archive2)
+archive2 = admin_project.repository_archive("master")
+assert archive1 == archive2
snapshot = admin_project.snapshot()
# project file uploads
filename = "test.txt"
file_contents = "testing contents"
uploaded_file = admin_project.upload(filename, file_contents)
-assert(uploaded_file["alt"] == filename)
-assert(uploaded_file["url"].startswith("/uploads/"))
-assert(uploaded_file["url"].endswith("/" + filename))
-assert(uploaded_file["markdown"] == "[{}]({})".format(
- uploaded_file["alt"],
- uploaded_file["url"],
-))
+assert uploaded_file["alt"] == filename
+assert uploaded_file["url"].startswith("/uploads/")
+assert uploaded_file["url"].endswith("/" + filename)
+assert uploaded_file["markdown"] == "[{}]({})".format(
+ uploaded_file["alt"], uploaded_file["url"]
+)
# environments
-admin_project.environments.create({'name': 'env1', 'external_url':
- 'http://fake.env/whatever'})
+admin_project.environments.create(
+ {"name": "env1", "external_url": "http://fake.env/whatever"}
+)
envs = admin_project.environments.list()
-assert(len(envs) == 1)
+assert len(envs) == 1
env = envs[0]
-env.external_url = 'http://new.env/whatever'
+env.external_url = "http://new.env/whatever"
env.save()
env = admin_project.environments.list()[0]
-assert(env.external_url == 'http://new.env/whatever')
+assert env.external_url == "http://new.env/whatever"
env.stop()
env.delete()
-assert(len(admin_project.environments.list()) == 0)
+assert len(admin_project.environments.list()) == 0
# project events
admin_project.events.list()
# forks
-fork = admin_project.forks.create({'namespace': user1.username})
+fork = admin_project.forks.create({"namespace": user1.username})
p = gl.projects.get(fork.id)
-assert(p.forked_from_project['id'] == admin_project.id)
+assert p.forked_from_project["id"] == admin_project.id
forks = admin_project.forks.list()
-assert(fork.id in map(lambda p: p.id, forks))
+assert fork.id in map(lambda p: p.id, forks)
# project hooks
-hook = admin_project.hooks.create({'url': 'http://hook.url'})
-assert(len(admin_project.hooks.list()) == 1)
+hook = admin_project.hooks.create({"url": "http://hook.url"})
+assert len(admin_project.hooks.list()) == 1
hook.note_events = True
hook.save()
hook = admin_project.hooks.get(hook.id)
-assert(hook.note_events is True)
+assert hook.note_events is True
hook.delete()
# deploy keys
-deploy_key = admin_project.keys.create({'title': 'foo@bar', 'key': DEPLOY_KEY})
+deploy_key = admin_project.keys.create({"title": "foo@bar", "key": DEPLOY_KEY})
project_keys = list(admin_project.keys.list())
-assert(len(project_keys) == 1)
+assert len(project_keys) == 1
sudo_project.keys.enable(deploy_key.id)
-assert(len(sudo_project.keys.list()) == 1)
+assert len(sudo_project.keys.list()) == 1
sudo_project.keys.delete(deploy_key.id)
-assert(len(sudo_project.keys.list()) == 0)
+assert len(sudo_project.keys.list()) == 0
# labels
-label1 = admin_project.labels.create({'name': 'label1', 'color': '#778899'})
+label1 = admin_project.labels.create({"name": "label1", "color": "#778899"})
label1 = admin_project.labels.list()[0]
-assert(len(admin_project.labels.list()) == 1)
-label1.new_name = 'label1updated'
+assert len(admin_project.labels.list()) == 1
+label1.new_name = "label1updated"
label1.save()
-assert(label1.name == 'label1updated')
+assert label1.name == "label1updated"
label1.subscribe()
-assert(label1.subscribed == True)
+assert label1.subscribed == True
label1.unsubscribe()
-assert(label1.subscribed == False)
+assert label1.subscribed == False
label1.delete()
# milestones
-m1 = admin_project.milestones.create({'title': 'milestone1'})
-assert(len(admin_project.milestones.list()) == 1)
-m1.due_date = '2020-01-01T00:00:00Z'
+m1 = admin_project.milestones.create({"title": "milestone1"})
+assert len(admin_project.milestones.list()) == 1
+m1.due_date = "2020-01-01T00:00:00Z"
m1.save()
-m1.state_event = 'close'
+m1.state_event = "close"
m1.save()
m1 = admin_project.milestones.get(m1.id)
-assert(m1.state == 'closed')
-assert(len(m1.issues()) == 0)
-assert(len(m1.merge_requests()) == 0)
+assert m1.state == "closed"
+assert len(m1.issues()) == 0
+assert len(m1.merge_requests()) == 0
# issues
-issue1 = admin_project.issues.create({'title': 'my issue 1',
- 'milestone_id': m1.id})
-issue2 = admin_project.issues.create({'title': 'my issue 2'})
-issue3 = admin_project.issues.create({'title': 'my issue 3'})
-assert(len(admin_project.issues.list()) == 3)
-issue3.state_event = 'close'
+issue1 = admin_project.issues.create({"title": "my issue 1", "milestone_id": m1.id})
+issue2 = admin_project.issues.create({"title": "my issue 2"})
+issue3 = admin_project.issues.create({"title": "my issue 3"})
+assert len(admin_project.issues.list()) == 3
+issue3.state_event = "close"
issue3.save()
-assert(len(admin_project.issues.list(state='closed')) == 1)
-assert(len(admin_project.issues.list(state='opened')) == 2)
-assert(len(admin_project.issues.list(milestone='milestone1')) == 1)
-assert(m1.issues().next().title == 'my issue 1')
-note = issue1.notes.create({'body': 'This is an issue note'})
-assert(len(issue1.notes.list()) == 1)
-emoji = note.awardemojis.create({'name': 'tractor'})
-assert(len(note.awardemojis.list()) == 1)
+assert len(admin_project.issues.list(state="closed")) == 1
+assert len(admin_project.issues.list(state="opened")) == 2
+assert len(admin_project.issues.list(milestone="milestone1")) == 1
+assert m1.issues().next().title == "my issue 1"
+note = issue1.notes.create({"body": "This is an issue note"})
+assert len(issue1.notes.list()) == 1
+emoji = note.awardemojis.create({"name": "tractor"})
+assert len(note.awardemojis.list()) == 1
emoji.delete()
-assert(len(note.awardemojis.list()) == 0)
+assert len(note.awardemojis.list()) == 0
note.delete()
-assert(len(issue1.notes.list()) == 0)
-assert(isinstance(issue1.user_agent_detail(), dict))
+assert len(issue1.notes.list()) == 0
+assert isinstance(issue1.user_agent_detail(), dict)
-assert(issue1.user_agent_detail()['user_agent'])
-assert(issue1.participants())
+assert issue1.user_agent_detail()["user_agent"]
+assert issue1.participants()
# issues labels and events
-label2 = admin_project.labels.create({'name': 'label2', 'color': '#aabbcc'})
-issue1.labels = ['label2']
+label2 = admin_project.labels.create({"name": "label2", "color": "#aabbcc"})
+issue1.labels = ["label2"]
issue1.save()
events = issue1.resourcelabelevents.list()
-assert(events)
+assert events
event = issue1.resourcelabelevents.get(events[0].id)
-assert(event)
+assert event
-discussion = issue1.discussions.create({'body': 'Discussion body'})
-assert(len(issue1.discussions.list()) == 1)
-d_note = discussion.notes.create({'body': 'first note'})
+discussion = issue1.discussions.create({"body": "Discussion body"})
+assert len(issue1.discussions.list()) == 1
+d_note = discussion.notes.create({"body": "first note"})
d_note_from_get = discussion.notes.get(d_note.id)
-d_note_from_get.body = 'updated body'
+d_note_from_get.body = "updated body"
d_note_from_get.save()
discussion = issue1.discussions.get(discussion.id)
-assert(discussion.attributes['notes'][-1]['body'] == 'updated body')
+assert discussion.attributes["notes"][-1]["body"] == "updated body"
d_note_from_get.delete()
discussion = issue1.discussions.get(discussion.id)
-assert(len(discussion.attributes['notes']) == 1)
+assert len(discussion.attributes["notes"]) == 1
# tags
-tag1 = admin_project.tags.create({'tag_name': 'v1.0', 'ref': 'master'})
-assert(len(admin_project.tags.list()) == 1)
-tag1.set_release_description('Description 1')
-tag1.set_release_description('Description 2')
-assert(tag1.release['description'] == 'Description 2')
+tag1 = admin_project.tags.create({"tag_name": "v1.0", "ref": "master"})
+assert len(admin_project.tags.list()) == 1
+tag1.set_release_description("Description 1")
+tag1.set_release_description("Description 2")
+assert tag1.release["description"] == "Description 2"
tag1.delete()
# project snippet
admin_project.snippets_enabled = True
admin_project.save()
snippet = admin_project.snippets.create(
- {'title': 'snip1', 'file_name': 'foo.py', 'code': 'initial content',
- 'visibility': gitlab.v4.objects.VISIBILITY_PRIVATE}
+ {
+ "title": "snip1",
+ "file_name": "foo.py",
+ "code": "initial content",
+ "visibility": gitlab.v4.objects.VISIBILITY_PRIVATE,
+ }
)
-assert(snippet.user_agent_detail()['user_agent'])
+assert snippet.user_agent_detail()["user_agent"]
-discussion = snippet.discussions.create({'body': 'Discussion body'})
-assert(len(snippet.discussions.list()) == 1)
-d_note = discussion.notes.create({'body': 'first note'})
+discussion = snippet.discussions.create({"body": "Discussion body"})
+assert len(snippet.discussions.list()) == 1
+d_note = discussion.notes.create({"body": "first note"})
d_note_from_get = discussion.notes.get(d_note.id)
-d_note_from_get.body = 'updated body'
+d_note_from_get.body = "updated body"
d_note_from_get.save()
discussion = snippet.discussions.get(discussion.id)
-assert(discussion.attributes['notes'][-1]['body'] == 'updated body')
+assert discussion.attributes["notes"][-1]["body"] == "updated body"
d_note_from_get.delete()
discussion = snippet.discussions.get(discussion.id)
-assert(len(discussion.attributes['notes']) == 1)
+assert len(discussion.attributes["notes"]) == 1
-snippet.file_name = 'bar.py'
+snippet.file_name = "bar.py"
snippet.save()
snippet = admin_project.snippets.get(snippet.id)
-assert(snippet.content().decode() == 'initial content')
-assert(snippet.file_name == 'bar.py')
+assert snippet.content().decode() == "initial content"
+assert snippet.file_name == "bar.py"
size = len(admin_project.snippets.list())
snippet.delete()
-assert(len(admin_project.snippets.list()) == (size - 1))
+assert len(admin_project.snippets.list()) == (size - 1)
# triggers
-tr1 = admin_project.triggers.create({'description': 'trigger1'})
-assert(len(admin_project.triggers.list()) == 1)
+tr1 = admin_project.triggers.create({"description": "trigger1"})
+assert len(admin_project.triggers.list()) == 1
tr1.delete()
# variables
-v1 = admin_project.variables.create({'key': 'key1', 'value': 'value1'})
-assert(len(admin_project.variables.list()) == 1)
-v1.value = 'new_value1'
+v1 = admin_project.variables.create({"key": "key1", "value": "value1"})
+assert len(admin_project.variables.list()) == 1
+v1.value = "new_value1"
v1.save()
v1 = admin_project.variables.get(v1.key)
-assert(v1.value == 'new_value1')
+assert v1.value == "new_value1"
v1.delete()
# branches and merges
-to_merge = admin_project.branches.create({'branch': 'branch1',
- 'ref': 'master'})
-admin_project.files.create({'file_path': 'README2.rst',
- 'branch': 'branch1',
- 'content': 'Initial content',
- 'commit_message': 'New commit in new branch'})
-mr = admin_project.mergerequests.create({'source_branch': 'branch1',
- 'target_branch': 'master',
- 'title': 'MR readme2'})
+to_merge = admin_project.branches.create({"branch": "branch1", "ref": "master"})
+admin_project.files.create(
+ {
+ "file_path": "README2.rst",
+ "branch": "branch1",
+ "content": "Initial content",
+ "commit_message": "New commit in new branch",
+ }
+)
+mr = admin_project.mergerequests.create(
+ {"source_branch": "branch1", "target_branch": "master", "title": "MR readme2"}
+)
# discussion
-discussion = mr.discussions.create({'body': 'Discussion body'})
-assert(len(mr.discussions.list()) == 1)
-d_note = discussion.notes.create({'body': 'first note'})
+discussion = mr.discussions.create({"body": "Discussion body"})
+assert len(mr.discussions.list()) == 1
+d_note = discussion.notes.create({"body": "first note"})
d_note_from_get = discussion.notes.get(d_note.id)
-d_note_from_get.body = 'updated body'
+d_note_from_get.body = "updated body"
d_note_from_get.save()
discussion = mr.discussions.get(discussion.id)
-assert(discussion.attributes['notes'][-1]['body'] == 'updated body')
+assert discussion.attributes["notes"][-1]["body"] == "updated body"
d_note_from_get.delete()
discussion = mr.discussions.get(discussion.id)
-assert(len(discussion.attributes['notes']) == 1)
+assert len(discussion.attributes["notes"]) == 1
# mr labels and events
-mr.labels = ['label2']
+mr.labels = ["label2"]
mr.save()
events = mr.resourcelabelevents.list()
-assert(events)
+assert events
event = mr.resourcelabelevents.get(events[0].id)
-assert(event)
+assert event
# basic testing: only make sure that the methods exist
mr.commits()
mr.changes()
-assert(mr.participants())
+assert mr.participants()
mr.merge()
-admin_project.branches.delete('branch1')
+admin_project.branches.delete("branch1")
try:
mr.merge()
@@ -660,52 +692,52 @@ except gitlab.GitlabMRClosedError:
pass
# protected branches
-p_b = admin_project.protectedbranches.create({'name': '*-stable'})
-assert(p_b.name == '*-stable')
-p_b = admin_project.protectedbranches.get('*-stable')
+p_b = admin_project.protectedbranches.create({"name": "*-stable"})
+assert p_b.name == "*-stable"
+p_b = admin_project.protectedbranches.get("*-stable")
# master is protected by default when a branch has been created
-assert(len(admin_project.protectedbranches.list()) == 2)
-admin_project.protectedbranches.delete('master')
+assert len(admin_project.protectedbranches.list()) == 2
+admin_project.protectedbranches.delete("master")
p_b.delete()
-assert(len(admin_project.protectedbranches.list()) == 0)
+assert len(admin_project.protectedbranches.list()) == 0
# stars
admin_project.star()
-assert(admin_project.star_count == 1)
+assert admin_project.star_count == 1
admin_project.unstar()
-assert(admin_project.star_count == 0)
+assert admin_project.star_count == 0
# project boards
-#boards = admin_project.boards.list()
-#assert(len(boards))
-#board = boards[0]
-#lists = board.lists.list()
-#begin_size = len(lists)
-#last_list = lists[-1]
-#last_list.position = 0
-#last_list.save()
-#last_list.delete()
-#lists = board.lists.list()
-#assert(len(lists) == begin_size - 1)
+# boards = admin_project.boards.list()
+# assert(len(boards))
+# board = boards[0]
+# lists = board.lists.list()
+# begin_size = len(lists)
+# last_list = lists[-1]
+# last_list.position = 0
+# last_list.save()
+# last_list.delete()
+# lists = board.lists.list()
+# assert(len(lists) == begin_size - 1)
# project badges
-badge_image = 'http://example.com'
-badge_link = 'http://example/img.svg'
-badge = admin_project.badges.create({'link_url': badge_link, 'image_url': badge_image})
-assert(len(admin_project.badges.list()) == 1)
-badge.image_url = 'http://another.example.com'
+badge_image = "http://example.com"
+badge_link = "http://example/img.svg"
+badge = admin_project.badges.create({"link_url": badge_link, "image_url": badge_image})
+assert len(admin_project.badges.list()) == 1
+badge.image_url = "http://another.example.com"
badge.save()
badge = admin_project.badges.get(badge.id)
-assert(badge.image_url == 'http://another.example.com')
+assert badge.image_url == "http://another.example.com"
badge.delete()
-assert(len(admin_project.badges.list()) == 0)
+assert len(admin_project.badges.list()) == 0
# project wiki
-wiki_content = 'Wiki page content'
-wp = admin_project.wikis.create({'title': 'wikipage', 'content': wiki_content})
-assert(len(admin_project.wikis.list()) == 1)
+wiki_content = "Wiki page content"
+wp = admin_project.wikis.create({"title": "wikipage", "content": wiki_content})
+assert len(admin_project.wikis.list()) == 1
wp = admin_project.wikis.get(wp.slug)
-assert(wp.content == wiki_content)
+assert wp.content == wiki_content
# update and delete seem broken
# wp.content = 'new content'
# wp.save()
@@ -714,66 +746,67 @@ assert(wp.content == wiki_content)
# namespaces
ns = gl.namespaces.list(all=True)
-assert(len(ns) != 0)
-ns = gl.namespaces.list(search='root', all=True)[0]
-assert(ns.kind == 'user')
+assert len(ns) != 0
+ns = gl.namespaces.list(search="root", all=True)[0]
+assert ns.kind == "user"
# features
-feat = gl.features.set('foo', 30)
-assert(feat.name == 'foo')
-assert(len(gl.features.list()) == 1)
+feat = gl.features.set("foo", 30)
+assert feat.name == "foo"
+assert len(gl.features.list()) == 1
feat.delete()
-assert(len(gl.features.list()) == 0)
+assert len(gl.features.list()) == 0
# broadcast messages
-msg = gl.broadcastmessages.create({'message': 'this is the message'})
-msg.color = '#444444'
+msg = gl.broadcastmessages.create({"message": "this is the message"})
+msg.color = "#444444"
msg.save()
msg = gl.broadcastmessages.list(all=True)[0]
-assert(msg.color == '#444444')
+assert msg.color == "#444444"
msg = gl.broadcastmessages.get(1)
-assert(msg.color == '#444444')
+assert msg.color == "#444444"
msg.delete()
-assert(len(gl.broadcastmessages.list()) == 0)
+assert len(gl.broadcastmessages.list()) == 0
# notification settings
settings = gl.notificationsettings.get()
settings.level = gitlab.NOTIFICATION_LEVEL_WATCH
settings.save()
settings = gl.notificationsettings.get()
-assert(settings.level == gitlab.NOTIFICATION_LEVEL_WATCH)
+assert settings.level == gitlab.NOTIFICATION_LEVEL_WATCH
# services
-service = admin_project.services.get('asana')
-service.api_key = 'whatever'
+service = admin_project.services.get("asana")
+service.api_key = "whatever"
service.save()
-service = admin_project.services.get('asana')
-assert(service.active == True)
+service = admin_project.services.get("asana")
+assert service.active == True
service.delete()
-service = admin_project.services.get('asana')
-assert(service.active == False)
+service = admin_project.services.get("asana")
+assert service.active == False
# snippets
snippets = gl.snippets.list(all=True)
-assert(len(snippets) == 0)
-snippet = gl.snippets.create({'title': 'snippet1', 'file_name': 'snippet1.py',
- 'content': 'import gitlab'})
+assert len(snippets) == 0
+snippet = gl.snippets.create(
+ {"title": "snippet1", "file_name": "snippet1.py", "content": "import gitlab"}
+)
snippet = gl.snippets.get(snippet.id)
-snippet.title = 'updated_title'
+snippet.title = "updated_title"
snippet.save()
snippet = gl.snippets.get(snippet.id)
-assert(snippet.title == 'updated_title')
+assert snippet.title == "updated_title"
content = snippet.content()
-assert(content.decode() == 'import gitlab')
+assert content.decode() == "import gitlab"
-assert(snippet.user_agent_detail()['user_agent'])
+assert snippet.user_agent_detail()["user_agent"]
snippet.delete()
snippets = gl.snippets.list(all=True)
-assert(len(snippets) == 0)
+assert len(snippets) == 0
# user activities
-gl.user_activities.list(query_parameters={'from': '2019-01-01'})
+gl.user_activities.list(query_parameters={"from": "2019-01-01"})
# events
gl.events.list()
@@ -786,19 +819,18 @@ settings.throttle_authenticated_api_period_in_seconds = 3
settings.save()
projects = list()
for i in range(0, 20):
- projects.append(gl.projects.create(
- {'name': str(i) + "ok"}))
+ projects.append(gl.projects.create({"name": str(i) + "ok"}))
error_message = None
for i in range(20, 40):
try:
projects.append(
- gl.projects.create(
- {'name': str(i) + 'shouldfail'}, obey_rate_limit=False))
+ gl.projects.create({"name": str(i) + "shouldfail"}, obey_rate_limit=False)
+ )
except gitlab.GitlabCreateError as e:
error_message = e.error_message
break
-assert 'Retry later' in error_message
+assert "Retry later" in error_message
[current_project.delete() for current_project in projects]
settings.throttle_authenticated_api_enabled = False
settings.save()
@@ -807,22 +839,23 @@ settings.save()
ex = admin_project.exports.create({})
ex.refresh()
count = 0
-while ex.export_status != 'finished':
+while ex.export_status != "finished":
time.sleep(1)
ex.refresh()
count += 1
if count == 10:
- raise Exception('Project export taking too much time')
-with open('/tmp/gitlab-export.tgz', 'wb') as f:
+ raise Exception("Project export taking too much time")
+with open("/tmp/gitlab-export.tgz", "wb") as f:
ex.download(streamed=True, action=f.write)
-output = gl.projects.import_project(open('/tmp/gitlab-export.tgz', 'rb'),
- 'imported_project')
-project_import = gl.projects.get(output['id'], lazy=True).imports.get()
+output = gl.projects.import_project(
+ open("/tmp/gitlab-export.tgz", "rb"), "imported_project"
+)
+project_import = gl.projects.get(output["id"], lazy=True).imports.get()
count = 0
-while project_import.import_status != 'finished':
+while project_import.import_status != "finished":
time.sleep(1)
project_import.refresh()
count += 1
if count == 10:
- raise Exception('Project import taking too much time')
+ raise Exception("Project import taking too much time")