glpkg 0.0.1__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glpkg
3
- Version: 0.0.1
3
+ Version: 1.1.0
4
4
  Summary: Tool to make GitLab generic package registry operations easy.
5
5
  Author-email: bugproduction <bugproduction@outlook.com>
6
6
  License-Expression: MIT
@@ -124,4 +124,3 @@ The tool is not perfect (yet) and has limitations. The following limitations are
124
124
 
125
125
  - Uploading files must be done one-by-one.
126
126
  - Only project registries are supported for now.
127
- - Pagination is not supported for now - in case you have more than 100 versions of a package, not all will be shown.
@@ -104,5 +104,4 @@ To use the `CI_JOB_TOKEN` with package registry of another projects, add `--proj
104
104
  The tool is not perfect (yet) and has limitations. The following limitations are known, but more can exist:
105
105
 
106
106
  - Uploading files must be done one-by-one.
107
- - Only project registries are supported for now.
108
- - Pagination is not supported for now - in case you have more than 100 versions of a package, not all will be shown.
107
+ - Only project registries are supported for now.
@@ -1,3 +1,3 @@
1
1
  from gitlab.packages import Packages
2
2
 
3
- __version__ = "0.0.1"
3
+ __version__ = "1.1.0"
@@ -1,4 +1,5 @@
1
1
  import argparse
2
+ import netrc
2
3
  import os
3
4
  from gitlab import Packages, __version__
4
5
 
@@ -47,7 +48,7 @@ class CLIHandler:
47
48
  "-c",
48
49
  "--ci",
49
50
  action="store_true",
50
- help="Use this to run the tool in GitLab pipelines. In this case CI_SERVER_HOST, CI_PROJECT_ID, and CI_JOB_TOKEN variables from the environment are used. --project and --token can be used to override project ID and the CI_JOB_TOKEN to a personal or project access token.",
51
+ help="Use this in GitLab jobs. In this case CI_SERVER_HOST, CI_PROJECT_ID, and CI_JOB_TOKEN variables from the environment are used. --project and --token can be used to override project ID and the CI_JOB_TOKEN to a personal or project access token.",
51
52
  )
52
53
  parser.add_argument(
53
54
  "-p",
@@ -56,12 +57,18 @@ class CLIHandler:
56
57
  help="The project ID or path. For example 123456 or namespace/project.",
57
58
  )
58
59
  parser.add_argument("-n", "--name", type=str, help="The package name.")
59
- parser.add_argument(
60
+ group2 = parser.add_mutually_exclusive_group()
61
+ group2.add_argument(
60
62
  "-t",
61
63
  "--token",
62
64
  type=str,
63
65
  help="Private or project access token that is used to authenticate with the package registry. Leave empty if the registry is public. The token must have 'read API' or 'API' scope.",
64
66
  )
67
+ group2.add_argument(
68
+ "--netrc",
69
+ action="store_true",
70
+ help="Set to use a token from .netrc file (~/.netrc) for the host. The .netrc username is ignored due to API restrictions. PRIVATE-TOKEN is used instead. Note that .netrc file access rights must be correct.",
71
+ )
65
72
 
66
73
  def _register_download_parser(self, parser):
67
74
  self._register_common_arguments(parser)
@@ -71,35 +78,40 @@ class CLIHandler:
71
78
  def _args(self, args):
72
79
  if args.ci:
73
80
  host = os.environ["CI_SERVER_HOST"]
81
+ project = os.environ["CI_PROJECT_ID"]
82
+ token = os.environ["CI_JOB_TOKEN"]
83
+ token_user = "JOB-TOKEN"
74
84
  if args.project:
75
85
  project = args.project
76
- else:
77
- project = os.environ["CI_PROJECT_ID"]
78
86
  if args.token:
79
- token_user = "PRIVATE-TOKEN"
80
87
  token = args.token
81
- else:
82
- token_user = "JOB-TOKEN"
83
- token = os.environ["CI_JOB_TOKEN"]
88
+ token_user = "PRIVATE-TOKEN"
84
89
  else:
85
90
  host = args.host
86
91
  project = args.project
87
- token_user = "PRIVATE-TOKEN"
88
92
  token = args.token
93
+ token_user = "PRIVATE-TOKEN"
94
+ if args.netrc:
95
+ _, _, token = netrc.netrc().authenticators(host)
96
+ token_user = "PRIVATE-TOKEN"
89
97
  name = args.name
90
98
  return host, project, name, token_user, token
91
99
 
92
100
  def _download_handler(self, args) -> int:
101
+ ret = 1
93
102
  host, project, name, token_user, token = self._args(args)
94
103
  version = args.version
95
104
  gitlab = Packages(host, token_user, token)
96
105
  package_id = gitlab.get_package_id(project, name, version)
97
- files = gitlab.list_files(project, package_id)
98
- ret = 1
99
- for file in files:
100
- ret = gitlab.download_file(project, name, version, file)
101
- if not ret:
102
- break
106
+ if package_id:
107
+ files = gitlab.list_files(project, package_id)
108
+ for file in files:
109
+ ret = gitlab.download_file(project, name, version, file)
110
+ if ret:
111
+ print("Failed to download file " + file)
112
+ break
113
+ else:
114
+ print("No package " + name + " version " + version + " found!")
103
115
  return ret
104
116
 
105
117
  def _register_list_parser(self, parser):
@@ -126,9 +138,13 @@ class CLIHandler:
126
138
  parser.set_defaults(action=self._upload)
127
139
 
128
140
  def _upload(self, args) -> int:
141
+ ret = 1
129
142
  host, project, name, token_user, token = self._args(args)
130
143
  version = args.version
131
144
  file = args.file
132
- gitlab = Packages(host, token_user, token)
133
- ret = gitlab.upload_file(project, name, version, file)
145
+ if os.path.isfile(file):
146
+ gitlab = Packages(host, token_user, token)
147
+ ret = gitlab.upload_file(project, name, version, file)
148
+ else:
149
+ print("File " + file + " does not exist!")
134
150
  return ret
@@ -0,0 +1,163 @@
1
+ from http.client import HTTPMessage
2
+ import json
3
+ import logging
4
+ from urllib import request, parse
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class Packages:
10
+ def __init__(self, host: str, token_type: str, token: str):
11
+ self.host = host
12
+ self.token_type = token_type
13
+ self.token = token
14
+
15
+ def api_url(self) -> str:
16
+ return "https://{}/api/v4/".format(parse.quote(self.host))
17
+
18
+ def project_api_url(self, project: str) -> str:
19
+ return self.api_url() + "projects/{}/".format(parse.quote_plus(project))
20
+
21
+ def get_headers(self) -> dict:
22
+ headers = {}
23
+ if self.token_type and self.token:
24
+ headers = {self.token_type: self.token}
25
+ return headers
26
+
27
+ def _request(self, url: str) -> tuple[int, bytes, HTTPMessage]:
28
+ logger.debug("Requesting " + url)
29
+ req = request.Request(url, headers=self.get_headers())
30
+ with request.urlopen(req) as response:
31
+ return response.status, response.read(), response.headers
32
+
33
+ def _get_next_page(self, headers: HTTPMessage) -> int:
34
+ ret = 0
35
+ if headers:
36
+ next_page = headers.get("x-next-page")
37
+ if next_page:
38
+ ret = int(next_page)
39
+ logger.debug("Response incomplete, next page is " + next_page)
40
+ else:
41
+ logger.debug("Response complete")
42
+ return ret
43
+
44
+ def _build_query(self, arg: str, page: int) -> str:
45
+ query = ""
46
+ if arg or page:
47
+ if page:
48
+ page = "page=" + str(page)
49
+ query = "?{}".format("&".join(filter(None, (arg, page))))
50
+ return query
51
+
52
+ def gl_project_api(self, project: str, path: str, arg: str = None) -> list:
53
+ data = []
54
+ more = True
55
+ page = None
56
+ while more:
57
+ more = False
58
+ query = self._build_query(arg, page)
59
+ url = self.project_api_url(project) + path + query
60
+ status, res_data, headers = self._request(url)
61
+ logger.debug("Response status: " + str(status))
62
+ res_data = json.loads(res_data)
63
+ logger.debug("Response data: " + str(res_data))
64
+ data = data + res_data
65
+ page = self._get_next_page(headers)
66
+ if page:
67
+ more = True
68
+ return data
69
+
70
+ def list_packages(self, project: str, package_name: str) -> list:
71
+ packages = []
72
+ logger.debug("Listing packages with name " + package_name)
73
+ data = self.gl_project_api(
74
+ project, "packages", "package_name=" + parse.quote_plus(package_name)
75
+ )
76
+ for package in data:
77
+ name = parse.unquote(package["name"])
78
+ version = parse.unquote(package["version"])
79
+ # GitLab API returns packages that have some match to the filter;
80
+ # let's filter out non-exact matches
81
+ if package_name != name:
82
+ continue
83
+ packages.append({"name": name, "version": version})
84
+ return packages
85
+
86
+ def list_files(self, project: str, package_id: int) -> list:
87
+ files = []
88
+ logger.debug("Listing package " + str(package_id) + " files")
89
+ path = "packages/" + parse.quote_plus(str(package_id)) + "/package_files"
90
+ data = self.gl_project_api(project, path)
91
+ for package in data:
92
+ # Only append the filename once to the list of files
93
+ # as there's no way to download them separately through
94
+ # the API
95
+ filename = parse.unquote(package["file_name"])
96
+ if filename not in files:
97
+ files.append(filename)
98
+ return files
99
+
100
+ def get_package_id(
101
+ self, project: str, package_name: str, package_version: str
102
+ ) -> int:
103
+ id = 0
104
+ logger.debug(
105
+ "Fetching package " + package_name + " (" + package_version + ") ID"
106
+ )
107
+ path = "packages"
108
+ arg = (
109
+ "package_name="
110
+ + parse.quote_plus(package_name)
111
+ + "&package_version="
112
+ + parse.quote_plus(package_version)
113
+ )
114
+ data = self.gl_project_api(project, path, arg)
115
+ if len(data) == 1:
116
+ package = data.pop()
117
+ id = package["id"]
118
+ return id
119
+
120
+ def download_file(
121
+ self, project: str, package_name: str, package_version: str, file: str
122
+ ) -> int:
123
+ ret = 1
124
+ logger.debug("Downloading file " + file)
125
+ url = (
126
+ self.project_api_url(project)
127
+ + "packages/generic/"
128
+ + parse.quote_plus(package_name)
129
+ + "/"
130
+ + parse.quote_plus(package_version)
131
+ + "/"
132
+ + parse.quote(str(file))
133
+ )
134
+ status, data, _ = self._request(url)
135
+ if status == 200:
136
+ with open(str(file), "wb") as file:
137
+ file.write(data)
138
+ ret = 0
139
+ return ret
140
+
141
+ def upload_file(
142
+ self, project: str, package_name: str, package_version: str, file: str
143
+ ) -> int:
144
+ ret = 1
145
+ logger.debug("Uploading file " + file)
146
+ with open(str(file), "rb") as data:
147
+ url = (
148
+ self.project_api_url(project)
149
+ + "packages/generic/"
150
+ + parse.quote_plus(package_name)
151
+ + "/"
152
+ + parse.quote_plus(package_version)
153
+ + "/"
154
+ + parse.quote(str(file))
155
+ )
156
+ res = request.urlopen(
157
+ request.Request(
158
+ url, method="PUT", data=data, headers=self.get_headers()
159
+ )
160
+ )
161
+ if res.status == 201: # 201 is created
162
+ ret = 0
163
+ return ret
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glpkg
3
- Version: 0.0.1
3
+ Version: 1.1.0
4
4
  Summary: Tool to make GitLab generic package registry operations easy.
5
5
  Author-email: bugproduction <bugproduction@outlook.com>
6
6
  License-Expression: MIT
@@ -124,4 +124,3 @@ The tool is not perfect (yet) and has limitations. The following limitations are
124
124
 
125
125
  - Uploading files must be done one-by-one.
126
126
  - Only project registries are supported for now.
127
- - Pagination is not supported for now - in case you have more than 100 versions of a package, not all will be shown.
@@ -11,4 +11,5 @@ src/glpkg.egg-info/dependency_links.txt
11
11
  src/glpkg.egg-info/entry_points.txt
12
12
  src/glpkg.egg-info/requires.txt
13
13
  src/glpkg.egg-info/top_level.txt
14
- test/test_gitlab.py
14
+ test/test_cli_handler.py
15
+ test/test_packages.py
@@ -0,0 +1,45 @@
1
+ from unittest.mock import patch
2
+
3
+ import urllib
4
+
5
+ from gitlab import __version__
6
+ from gitlab.cli_handler import CLIHandler
7
+ import sys
8
+
9
+ from utils import mock_empty_response, mock_one_response
10
+
11
+
12
+ class TestCliHandler:
13
+
14
+ def test_version(self, capsys):
15
+ args = ["glpkg", "-v"]
16
+ with patch.object(sys, "argv", args):
17
+ handler = CLIHandler()
18
+ handler.do_it()
19
+ out, err = capsys.readouterr()
20
+ assert out == __version__ + "\n"
21
+ assert err == ""
22
+
23
+ def test_list_empty(self, mock_empty_response, capsys):
24
+ args = ["glpkg", "list", "--project", "18105942", "--name", "AABCComponent"]
25
+ with patch.object(sys, "argv", args):
26
+ with patch.object(
27
+ urllib.request, "urlopen", return_value=mock_empty_response
28
+ ):
29
+ handler = CLIHandler()
30
+ handler.do_it()
31
+ out, err = capsys.readouterr()
32
+ assert out == "Name\t\tVersion\n"
33
+ assert err == ""
34
+
35
+ def test_list_one(self, mock_one_response, capsys):
36
+ args = ["glpkg", "list", "--project", "18105942", "--name", "ABCComponent"]
37
+ with patch.object(sys, "argv", args):
38
+ with patch.object(
39
+ urllib.request, "urlopen", return_value=mock_one_response
40
+ ):
41
+ handler = CLIHandler()
42
+ handler.do_it()
43
+ out, err = capsys.readouterr()
44
+ assert out == "Name\t\tVersion\nABCComponent\t0.0.1\n"
45
+ assert err == ""
@@ -1,14 +1,17 @@
1
- import io
2
- import pytest
3
1
  import urllib
4
2
  from gitlab.packages import *
5
3
  from unittest.mock import mock_open, patch
6
4
 
5
+ from utils import (
6
+ ResponseMock,
7
+ test_gitlab,
8
+ mock_empty_response,
9
+ mock_one_response,
10
+ mock_five_response,
11
+ )
7
12
 
8
- class TestGitLab:
9
- @pytest.fixture
10
- def test_gitlab(self):
11
- return Packages("gl-host", "token-name", "token-value")
13
+
14
+ class TestPackages:
12
15
 
13
16
  def test_api_url(self, test_gitlab):
14
17
  assert test_gitlab.api_url() == "https://gl-host/api/v4/"
@@ -33,79 +36,72 @@ class TestGitLab:
33
36
  test_gitlab = Packages("gl-host", "", "")
34
37
  assert test_gitlab.get_headers() == {}
35
38
 
36
- def test_list_packages_none(self, test_gitlab):
37
- data = io.StringIO("[]")
38
- with patch.object(urllib.request, "urlopen", return_value=data):
39
+ def test_list_packages_none(self, test_gitlab, mock_empty_response):
40
+ with patch.object(urllib.request, "urlopen", return_value=mock_empty_response):
39
41
  packages = test_gitlab.list_packages("24", "package-name")
40
42
  assert len(packages) == 0
41
43
 
42
- def test_list_packages_one(self, test_gitlab):
43
- data = io.StringIO('[{"name": "package-name", "version": "0.1.2"}]')
44
- with patch.object(urllib.request, "urlopen", return_value=data):
45
- packages = test_gitlab.list_packages("24", "package-name")
44
+ def test_list_packages_one(self, test_gitlab, mock_one_response):
45
+ with patch.object(urllib.request, "urlopen", return_value=mock_one_response):
46
+ packages = test_gitlab.list_packages("18105942", "ABCComponent")
46
47
  assert len(packages) == 1
47
48
 
48
49
  def test_list_name_packages_filter(self, test_gitlab):
49
- data = io.StringIO(
50
- '[{"name": "package-name", "version": "0.1.2"}, {"name": "package-name-something", "version": "0.1.2"}]'
50
+ data = ResponseMock(
51
+ 200,
52
+ "",
53
+ '[{"name": "package-name", "version": "0.1.2"}, {"name": "package-name-something", "version": "0.1.2"}]',
51
54
  )
52
55
  with patch.object(urllib.request, "urlopen", return_value=data):
53
56
  packages = test_gitlab.list_packages("24", "package-name")
54
57
  assert len(packages) == 1
55
58
 
56
- def test_list_name_packages_five(self, test_gitlab):
57
- data = io.StringIO(
58
- '[{"name": "package-name", "version": "0.1"}, {"name": "package-name", "version": "0.2"}, {"name": "package-name", "version": "0.3"}, {"name": "package-name", "version": "0.4"}, {"name": "package-name", "version": "0.5"}]'
59
- )
60
- with patch.object(urllib.request, "urlopen", return_value=data):
61
- packages = test_gitlab.list_packages("24", "package-name")
59
+ def test_list_name_packages_five(self, test_gitlab, mock_five_response):
60
+ with patch.object(urllib.request, "urlopen", return_value=mock_five_response):
61
+ packages = test_gitlab.list_packages("18105942", "ABCComponent")
62
62
  assert len(packages) == 5
63
63
 
64
- def test_list_files_none(self, test_gitlab):
65
- data = io.StringIO("[]")
66
- with patch.object(urllib.request, "urlopen", return_value=data):
64
+ def test_list_files_none(self, test_gitlab, mock_empty_response):
65
+ with patch.object(urllib.request, "urlopen", return_value=mock_empty_response):
67
66
  packages = test_gitlab.list_files("24", "123")
68
67
  assert len(packages) == 0
69
68
 
70
69
  def test_list_files_one(self, test_gitlab):
71
- data = io.StringIO('[{"file_name": "filea.txt"}]')
70
+ data = ResponseMock(200, "", '[{"file_name": "filea.txt"}]')
72
71
  with patch.object(urllib.request, "urlopen", return_value=data):
73
72
  packages = test_gitlab.list_files("24", "123")
74
73
  assert len(packages) == 1
75
74
 
76
75
  def test_list_files_five(self, test_gitlab):
77
- data = io.StringIO(
78
- '[{"file_name": "filea.txt"}, {"file_name": "fileb.txt"}, {"file_name": "filec.txt"}, {"file_name": "filed.txt"}, {"file_name": "filee.txt"}]'
76
+ data = ResponseMock(
77
+ 200,
78
+ "",
79
+ '[{"file_name": "filea.txt"}, {"file_name": "fileb.txt"}, {"file_name": "filec.txt"}, {"file_name": "filed.txt"}, {"file_name": "filee.txt"}]',
79
80
  )
80
81
  with patch.object(urllib.request, "urlopen", return_value=data):
81
82
  packages = test_gitlab.list_files("24", "123")
82
83
  assert len(packages) == 5
83
84
 
84
- def test_package_id_none(self, test_gitlab):
85
- data = io.StringIO("[]")
86
- with patch.object(urllib.request, "urlopen", return_value=data):
85
+ def test_package_id_none(self, test_gitlab, mock_empty_response):
86
+ with patch.object(urllib.request, "urlopen", return_value=mock_empty_response):
87
87
  packages = test_gitlab.get_package_id("24", "package-name", "0.1")
88
88
  assert packages == 0
89
89
 
90
90
  def test_package_id_one(self, test_gitlab):
91
- data = io.StringIO('[{"id": 123}]')
91
+ data = ResponseMock(200, "", '[{"id": 123}]')
92
92
  with patch.object(urllib.request, "urlopen", return_value=data):
93
93
  packages = test_gitlab.get_package_id("24", "package-name", "0.1")
94
94
  assert packages == 123
95
95
 
96
96
  def test_upload_file(self, test_gitlab):
97
- class rmock:
98
- def getcode():
99
- return 201
100
-
97
+ data = ResponseMock(201, "", "[]")
101
98
  with patch("builtins.open", mock_open(read_data="data")):
102
- with patch.object(urllib.request, "urlopen", return_value=rmock):
99
+ with patch.object(urllib.request, "urlopen", return_value=data):
103
100
  packages = test_gitlab.upload_file("24", "package-name", "0.1", "file")
104
101
  assert packages == 0
105
102
 
106
103
  def test_download_file(self, test_gitlab):
107
- data = io.StringIO("file-content")
108
- m = mock_open()
104
+ data = ResponseMock(200, "", "file-content")
109
105
  with patch("builtins.open", mock_open()) as file_mock:
110
106
  # mock_open.write.return_value = 0
111
107
  with patch.object(urllib.request, "urlopen", return_value=data):
@@ -1,130 +0,0 @@
1
- import json
2
- from urllib import request, parse
3
-
4
-
5
- class Packages:
6
- def __init__(self, host: str, token_type: str, token: str):
7
- self.host = host
8
- self.token_type = token_type
9
- self.token = token
10
-
11
- def api_url(self) -> str:
12
- return "https://{}/api/v4/".format(parse.quote(self.host))
13
-
14
- def project_api_url(self, project: str) -> str:
15
- return self.api_url() + "projects/{}/".format(parse.quote_plus(project))
16
-
17
- def get_headers(self):
18
- headers = {}
19
- if self.token_type and self.token:
20
- headers = {self.token_type: self.token}
21
- return headers
22
-
23
- def list_packages(self, project: str, package_name: str) -> list:
24
- packages = []
25
- with request.urlopen(
26
- request.Request(
27
- self.project_api_url(project)
28
- + "packages?package_name="
29
- + parse.quote_plus(package_name),
30
- headers=self.get_headers(),
31
- )
32
- ) as res:
33
- data = res.read()
34
- for package in json.loads(data):
35
- name = parse.unquote(package["name"])
36
- version = parse.unquote(package["version"])
37
- # The GitLab API returns packages that have some match to the filter;
38
- # let's filter out non-exact matches
39
- if package_name != name:
40
- continue
41
- packages.append({"name": name, "version": version})
42
- return packages
43
-
44
- def list_files(self, project: str, package_id: int) -> list:
45
- files = []
46
- with request.urlopen(
47
- request.Request(
48
- self.project_api_url(project)
49
- + "packages/"
50
- + parse.quote_plus(str(package_id))
51
- + "/package_files",
52
- headers=self.get_headers(),
53
- )
54
- ) as x:
55
- data = x.read()
56
- for package in json.loads(
57
- data,
58
- ):
59
- # Only append the filename once to the list of files
60
- # as there's no way to download them separately through
61
- # the API
62
- filename = parse.unquote(package["file_name"])
63
- if filename not in files:
64
- files.append(filename)
65
- return files
66
-
67
- def get_package_id(
68
- self, project: str, package_name: str, package_version: str
69
- ) -> int:
70
- id = 0
71
- with request.urlopen(
72
- request.Request(
73
- self.project_api_url(project)
74
- + "packages?package_name="
75
- + parse.quote_plus(package_name)
76
- + "&package_version="
77
- + parse.quote_plus(package_version),
78
- headers=self.get_headers(),
79
- )
80
- ) as res:
81
- data = res.read()
82
- package = json.loads(data)
83
- if len(package) == 1:
84
- package = package.pop()
85
- id = package["id"]
86
- return id
87
-
88
- def download_file(
89
- self, project: str, package_name: str, package_version: str, file: str
90
- ) -> int:
91
- ret = 1
92
- with request.urlopen(
93
- request.Request(
94
- self.project_api_url(project)
95
- + "packages/generic/"
96
- + parse.quote_plus(package_name)
97
- + "/"
98
- + parse.quote_plus(package_version)
99
- + "/"
100
- + parse.quote(str(file)),
101
- headers=self.get_headers(),
102
- )
103
- ) as req:
104
- with open(str(file), "wb") as file:
105
- file.write(req.read())
106
- ret = 0
107
- return ret
108
-
109
- def upload_file(
110
- self, project: str, package_name: str, package_version: str, file: str
111
- ) -> int:
112
- ret = 1
113
- with open(str(file), "rb") as data:
114
- res = request.urlopen(
115
- request.Request(
116
- self.project_api_url(project)
117
- + "packages/generic/"
118
- + parse.quote_plus(package_name)
119
- + "/"
120
- + parse.quote_plus(package_version)
121
- + "/"
122
- + parse.quote(str(file)),
123
- method="PUT",
124
- data=data,
125
- headers=self.get_headers(),
126
- )
127
- )
128
- if res.getcode() == 201: # 201 is created
129
- ret = 0
130
- return ret
File without changes
File without changes
File without changes
File without changes