Drop py2 support in gerrit and git related files

python3 is the only supported version of python in depot_tools.

Bug: 1475402
Change-Id: Ie4ee18d297081b3aa0206b8d7ce6461819bff0ca
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4809560
Reviewed-by: Josip Sokcevic <sokcevic@chromium.org>
Commit-Queue: Gavin Mak <gavinmak@google.com>
changes/60/4809560/7
Gavin Mak 2 years ago committed by LUCI CQ
parent 65c49b18b9
commit b5c7f4b46c

@ -9,20 +9,12 @@ Example usage:
./gerrit_client.py [command] [args]
"""
from __future__ import print_function
import json
import logging
import optparse
import subcommand
import sys
if sys.version_info.major == 2:
import urlparse
from urllib import quote_plus
else:
from urllib.parse import quote_plus
import urllib.parse as urlparse
import urllib.parse
import fix_encoding
import gerrit_util
@ -49,7 +41,7 @@ def CMDmovechanges(parser, args):
assert opt.destination_branch, "--destination_branch not defined"
for p in opt.params:
assert '=' in p, '--param is key=value, not "%s"' % p
host = urlparse.urlparse(opt.host).netloc
host = urllib.parse.urlparse(opt.host).netloc
limit = 100
while True:
@ -72,9 +64,9 @@ def CMDbranchinfo(parser, args):
parser.add_option('--branch', dest='branch', help='branch name')
(opt, args) = parser.parse_args(args)
host = urlparse.urlparse(opt.host).netloc
project = quote_plus(opt.project)
branch = quote_plus(opt.branch)
host = urllib.parse.urlparse(opt.host).netloc
project = urllib.parse.quote_plus(opt.project)
branch = urllib.parse.quote_plus(opt.branch)
result = gerrit_util.GetGerritBranch(host, project, branch)
logging.info(result)
write_result(result, opt)
@ -94,7 +86,7 @@ def CMDrawapi(parser, args):
(opt, args) = parser.parse_args(args)
assert opt.path, "--path not defined"
host = urlparse.urlparse(opt.host).netloc
host = urllib.parse.urlparse(opt.host).netloc
kwargs = {}
if opt.method:
kwargs['reqtype'] = opt.method.upper()
@ -122,9 +114,9 @@ def CMDbranch(parser, args):
assert opt.branch, "--branch not defined"
assert opt.commit, "--commit not defined"
project = quote_plus(opt.project)
host = urlparse.urlparse(opt.host).netloc
branch = quote_plus(opt.branch)
project = urllib.parse.quote_plus(opt.project)
host = urllib.parse.urlparse(opt.host).netloc
branch = urllib.parse.quote_plus(opt.branch)
result = gerrit_util.GetGerritBranch(host, project, branch)
if result:
if not opt.allow_existent_branch:
@ -161,9 +153,9 @@ def CMDtag(parser, args):
assert opt.tag, "--tag not defined"
assert opt.commit, "--commit not defined"
project = quote_plus(opt.project)
host = urlparse.urlparse(opt.host).netloc
tag = quote_plus(opt.tag)
project = urllib.parse.quote_plus(opt.project)
host = urllib.parse.urlparse(opt.host).netloc
tag = urllib.parse.quote_plus(opt.tag)
result = gerrit_util.CreateGerritTag(host, project, tag, opt.commit)
logging.info(result)
write_result(result, opt)
@ -178,9 +170,9 @@ def CMDhead(parser, args):
assert opt.project, "--project not defined"
assert opt.branch, "--branch not defined"
project = quote_plus(opt.project)
host = urlparse.urlparse(opt.host).netloc
branch = quote_plus(opt.branch)
project = urllib.parse.quote_plus(opt.project)
host = urllib.parse.urlparse(opt.host).netloc
branch = urllib.parse.quote_plus(opt.branch)
result = gerrit_util.UpdateHead(host, project, branch)
logging.info(result)
write_result(result, opt)
@ -193,8 +185,8 @@ def CMDheadinfo(parser, args):
(opt, args) = parser.parse_args(args)
assert opt.project, "--project not defined"
project = quote_plus(opt.project)
host = urlparse.urlparse(opt.host).netloc
project = urllib.parse.quote_plus(opt.project)
host = urllib.parse.urlparse(opt.host).netloc
result = gerrit_util.GetHead(host, project)
logging.info(result)
write_result(result, opt)
@ -224,7 +216,7 @@ def CMDchanges(parser, args):
assert '=' in p, '--param is key=value, not "%s"' % p
result = gerrit_util.QueryChanges(
urlparse.urlparse(opt.host).netloc,
urllib.parse.urlparse(opt.host).netloc,
list(tuple(p.split('=', 1)) for p in opt.params),
first_param=opt.query,
start=opt.start, # Default: None
@ -244,7 +236,7 @@ def CMDrelatedchanges(parser, args):
(opt, args) = parser.parse_args(args)
result = gerrit_util.GetRelatedChanges(
urlparse.urlparse(opt.host).netloc,
urllib.parse.urlparse(opt.host).netloc,
change=opt.change,
revision=opt.revision,
)
@ -282,7 +274,7 @@ def CMDcreatechange(parser, args):
params.append(('notify_details', {'CC': {'accounts': opt.cc_list}}))
result = gerrit_util.CreateChange(
urlparse.urlparse(opt.host).netloc,
urllib.parse.urlparse(opt.host).netloc,
opt.project,
branch=opt.branch,
subject=opt.subject,
@ -304,7 +296,7 @@ def CMDchangeedit(parser, args):
with open(opt.file) as f:
data = f.read()
result = gerrit_util.ChangeEdit(
urlparse.urlparse(opt.host).netloc, opt.change, opt.path, data)
urllib.parse.urlparse(opt.host).netloc, opt.change, opt.path, data)
logging.info(result)
write_result(result, opt)
@ -318,7 +310,7 @@ def CMDpublishchangeedit(parser, args):
(opt, args) = parser.parse_args(args)
result = gerrit_util.PublishChangeEdit(
urlparse.urlparse(opt.host).netloc, opt.change, opt.notify)
urllib.parse.urlparse(opt.host).netloc, opt.change, opt.notify)
logging.info(result)
write_result(result, opt)
@ -329,7 +321,7 @@ def CMDsubmitchange(parser, args):
parser.add_option('-c', '--change', type=int, help='change number')
(opt, args) = parser.parse_args(args)
result = gerrit_util.SubmitChange(
urlparse.urlparse(opt.host).netloc, opt.change)
urllib.parse.urlparse(opt.host).netloc, opt.change)
logging.info(result)
write_result(result, opt)
@ -340,7 +332,7 @@ def CMDchangesubmittedtogether(parser, args):
parser.add_option('-c', '--change', type=int, help='change number')
(opt, args) = parser.parse_args(args)
result = gerrit_util.GetChangesSubmittedTogether(
urlparse.urlparse(opt.host).netloc, opt.change)
urllib.parse.urlparse(opt.host).netloc, opt.change)
logging.info(result)
write_result(result, opt)
@ -351,7 +343,7 @@ def CMDgetcommitincludedin(parser, args):
parser.add_option('--commit', dest='commit', help='commit hash')
(opt, args) = parser.parse_args(args)
result = gerrit_util.GetCommitIncludedIn(
urlparse.urlparse(opt.host).netloc, opt.project, opt.commit)
urllib.parse.urlparse(opt.host).netloc, opt.project, opt.commit)
logging.info(result)
write_result(result, opt)
@ -361,8 +353,7 @@ def CMDsetbotcommit(parser, args):
"""Sets bot-commit+1 to a bot generated change."""
parser.add_option('-c', '--change', type=int, help='change number')
(opt, args) = parser.parse_args(args)
result = gerrit_util.SetReview(
urlparse.urlparse(opt.host).netloc,
result = gerrit_util.SetReview(urllib.parse.urlparse(opt.host).netloc,
opt.change,
labels={'Bot-Commit': 1},
ready=True)
@ -379,7 +370,7 @@ def CMDsetlabel(parser, args):
nargs=2,
metavar=('label_name', 'label_value'))
(opt, args) = parser.parse_args(args)
result = gerrit_util.SetReview(urlparse.urlparse(opt.host).netloc,
result = gerrit_util.SetReview(urllib.parse.urlparse(opt.host).netloc,
opt.change,
labels={opt.label[0]: opt.label[1]})
logging.info(result)
@ -395,8 +386,7 @@ def CMDabandon(parser, args):
(opt, args) = parser.parse_args(args)
assert opt.change, "-c not defined"
result = gerrit_util.AbandonChange(
urlparse.urlparse(opt.host).netloc,
opt.change, opt.message)
urllib.parse.urlparse(opt.host).netloc, opt.change, opt.message)
logging.info(result)
write_result(result, opt)
@ -439,7 +429,7 @@ def CMDmass_abandon(parser, args):
search_query.append(('status', 'open'))
logging.info("Searching for: %s" % search_query)
host = urlparse.urlparse(opt.host).netloc
host = urllib.parse.urlparse(opt.host).netloc
result = gerrit_util.QueryChanges(
host,

@ -8,9 +8,6 @@ Utilities for requesting information for a Gerrit server via HTTPS.
https://gerrit-review.googlesource.com/Documentation/rest-api.html
"""
from __future__ import print_function
from __future__ import unicode_literals
import base64
import contextlib
import httplib2
@ -33,14 +30,9 @@ import metrics
import metrics_utils
import subprocess2
from third_party import six
from six.moves import urllib
if sys.version_info.major == 2:
import cookielib
from StringIO import StringIO
else:
import http.cookiejar as cookielib
import http.cookiejar
from io import StringIO
LOGGER = logging.getLogger()
@ -255,7 +247,7 @@ class CookiesAuthenticator(Authenticator):
def _get_auth_for_host(self, host):
for domain, creds in self.gitcookies.items():
if cookielib.domain_match(host, domain):
if http.cookiejar.domain_match(host, domain):
return (creds[0], None, creds[1])
return self.netrc.authenticators(host)

@ -5,10 +5,7 @@
"""A git command for managing a local cache of git repositories."""
from __future__ import print_function
import contextlib
import errno
import logging
import optparse
import os
@ -18,11 +15,7 @@ import sys
import tempfile
import threading
import time
try:
import urlparse
except ImportError: # For Py3 compatibility
import urllib.parse as urlparse
import urllib.parse
from download_from_google_storage import Gsutil
import gclient_utils
@ -142,7 +135,7 @@ class Mirror(object):
b = os.getenv('OVERRIDE_BOOTSTRAP_BUCKET')
if b:
return b
u = urlparse.urlparse(self.url)
u = urllib.parse.urlparse(self.url)
if u.netloc == 'chromium.googlesource.com':
return 'chromium-git-cache'
# Not recognized.
@ -164,7 +157,7 @@ class Mirror(object):
url = os.path.splitdrive(url)[1]
return url.replace('-', '--').replace(os.sep, '-')
parsed = urlparse.urlparse(url)
parsed = urllib.parse.urlparse(url)
norm_url = parsed.netloc + parsed.path
if norm_url.endswith('.git'):
norm_url = norm_url[:-len('.git')]
@ -359,7 +352,7 @@ class Mirror(object):
def supported_project(self):
"""Returns true if this repo is known to have a bootstrap zip file."""
u = urlparse.urlparse(self.url)
u = urllib.parse.urlparse(self.url)
return u.netloc in [
'chromium.googlesource.com',
'chrome-internal.googlesource.com']

@ -7,8 +7,6 @@
"""A git-command for integrating reviews on Gerrit."""
from __future__ import print_function
import base64
import collections
import datetime
@ -55,14 +53,9 @@ import subprocess2
import swift_format
import watchlists
from third_party import six
from six.moves import urllib
if sys.version_info.major == 3:
basestring = (str,) # pylint: disable=redefined-builtin
__version__ = '2.0'
# Traces for git push will be stored in a traces directory inside the
@ -922,7 +915,7 @@ def ParseIssueNumberArgument(arg):
if isinstance(arg, int):
return _ParsedIssueNumberArgument(issue=arg)
if not isinstance(arg, basestring):
if not isinstance(arg, str):
return fail_result
if arg.isdigit():
@ -3179,7 +3172,7 @@ class ChangeDescription(object):
return '\n'.join(self._description_lines)
def set_description(self, desc):
if isinstance(desc, basestring):
if isinstance(desc, str):
lines = desc.splitlines()
else:
lines = [line.rstrip() for line in desc]

@ -13,9 +13,7 @@ from multiprocessing.pool import IMapIterator
def wrapper(func):
def wrap(self, timeout=None):
default_timeout = (1 << 31 if sys.version_info.major == 2 else
threading.TIMEOUT_MAX)
return func(self, timeout=timeout or default_timeout)
return func(self, timeout=timeout or threading.TIMEOUT_MAX)
return wrap
IMapIterator.next = wrapper(IMapIterator.next)

@ -3,8 +3,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import print_function
import argparse

@ -11,8 +11,6 @@ Note that it uses the "cherry picked from" annotation to find merges, so it will
only work on merges that followed the "use cherry-pick -x" instructions.
"""
from __future__ import print_function
import optparse
import re
import sys

@ -3,8 +3,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import print_function
import argparse
import json
import re

@ -6,9 +6,6 @@
"""Wrapper around git blame that ignores certain commits.
"""
from __future__ import print_function
from __future__ import unicode_literals
import argparse
import collections
import logging

@ -17,8 +17,6 @@ point to them. Items are colorized as follows:
* Blue background - The currently checked out commit
"""
from __future__ import unicode_literals
import os
import sys
@ -29,12 +27,6 @@ import subprocess2
from third_party import colorama
if sys.version_info.major == 2:
# On Python 3, BrokenPipeError is raised instead.
# pylint:disable=redefined-builtin
BrokenPipeError = IOError
RESET = colorama.Fore.RESET + colorama.Back.RESET + colorama.Style.RESET_ALL
BRIGHT = colorama.Style.BRIGHT

@ -24,8 +24,6 @@ Branches are colorized as follows:
upstream, then you will see this.
"""
from __future__ import print_function
import argparse
import collections
import metrics

@ -11,8 +11,6 @@ purposes of the chromium depot_tools git extensions. Passing no arguments will
just print the effective merge base for the current branch.
"""
from __future__ import print_function
import argparse
import sys

@ -9,8 +9,6 @@ is more than one downstream branch, then this script will prompt you to select
which branch.
"""
from __future__ import print_function
import argparse
import sys

@ -21,9 +21,6 @@ commit's entire history, this script caches all calculated data inside the git
repo that it operates on in the ref 'refs/number/commits'.
"""
from __future__ import print_function
from __future__ import division
import binascii
import collections
import logging
@ -60,11 +57,8 @@ def pathlify(hash_prefix):
>>> pathlify('\xDE\xAD')
'de/ad'
"""
if sys.version_info.major == 3:
return '/'.join('%02x' % b for b in hash_prefix)
return '/'.join('%02x' % ord(b) for b in hash_prefix)
@git.memoize_one(threadsafe=False)
def get_number_tree(prefix_bytes):
@ -184,10 +178,7 @@ def preload_tree(prefix):
def all_prefixes(depth=PREFIX_LEN):
if sys.version_info.major == 3:
prefixes = [bytes([i]) for i in range(255)]
else:
prefixes = [chr(i) for i in range(255)]
for x in prefixes:
# This isn't covered because PREFIX_LEN currently == 1
if depth > 1: # pragma: no cover

@ -7,8 +7,6 @@
Tool to update all branches to have the latest changes from their upstreams.
"""
from __future__ import print_function
import argparse
import collections
import logging

@ -5,8 +5,6 @@
"""Change the upstream of the current branch."""
from __future__ import print_function
import argparse
import sys

@ -3,8 +3,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import print_function
import argparse
import sys

@ -20,10 +20,6 @@ import gclient_utils
DEFAULT_BRANCH = 'main'
if sys.version_info.major == 3:
# pylint: disable=redefined-builtin
basestring = (str,)
def git_hash_data(data, typ='blob'):
"""Calculate the git-style SHA1 for some data.
@ -333,7 +329,7 @@ class GitRepo(object):
for fname, file_data in commit_data.items():
# If it isn't a string, it's one of the special keys.
if not isinstance(fname, basestring):
if not isinstance(fname, str):
continue
deleted = False
@ -496,8 +492,6 @@ class GitRepoSchemaTestBase(unittest.TestCase):
@classmethod
def getRepoContent(cls, commit):
commit = 'COMMIT_%s' % commit
if sys.version_info.major == 2:
commit = commit.encode('utf-8')
return getattr(cls, commit, None)
@classmethod

@ -9,20 +9,11 @@ import logging
import os
import sys
import unittest
if sys.version_info.major == 2:
from StringIO import StringIO
import mock
BUILTIN_OPEN = '__builtin__.open'
else:
from io import StringIO
from unittest import mock
BUILTIN_OPEN = 'builtins.open'
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import gerrit_client
import gerrit_util
class TestGerritClient(unittest.TestCase):
@ -120,7 +111,7 @@ class TestGerritClient(unittest.TestCase):
subject='subject',
params=[('work_in_progress', 'true')])
@mock.patch(BUILTIN_OPEN, mock.mock_open())
@mock.patch('builtins.open', mock.mock_open())
@mock.patch('gerrit_util.ChangeEdit', return_value='')
def test_changeedit(self, util_mock):
open().read.return_value = 'test_data'

@ -4,31 +4,19 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import print_function
from __future__ import unicode_literals
import base64
import httplib2
from io import StringIO
import json
import os
import socket
import sys
import unittest
if sys.version_info.major == 2:
from cStringIO import StringIO
import mock
else:
from io import StringIO
from unittest import mock
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import gerrit_util
import gclient_utils
import metrics
import metrics_utils
import subprocess2

@ -5,6 +5,7 @@
"""Unit tests for git_cache.py"""
from io import StringIO
import logging
import os
import shutil
@ -12,12 +13,6 @@ import subprocess
import sys
import tempfile
import unittest
if sys.version_info.major == 2:
from StringIO import StringIO
import mock
else:
from io import StringIO
from unittest import mock
DEPOT_TOOLS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

@ -6,26 +6,17 @@
"""Unit tests for git_cl.py."""
from __future__ import print_function
from __future__ import unicode_literals
import datetime
import json
import logging
from io import StringIO
import multiprocessing
import optparse
import os
import pprint
import shutil
import sys
import tempfile
import unittest
if sys.version_info.major == 2:
from StringIO import StringIO
import mock
else:
from io import StringIO
from unittest import mock
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

@ -6,9 +6,6 @@
"""Unit tests for git_common.py"""
from __future__ import print_function
from __future__ import unicode_literals
import binascii
import collections
import datetime

@ -5,25 +5,16 @@
# found in the LICENSE file.
"""Unit tests for git_find_releases.py."""
from __future__ import print_function
from __future__ import unicode_literals
from io import StringIO
import logging
import os
import sys
import unittest
if sys.version_info.major == 2:
from StringIO import StringIO
import mock
else:
from io import StringIO
from unittest import mock
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import git_find_releases
import git_common
class TestGitFindReleases(unittest.TestCase):

@ -2,17 +2,11 @@
"""Tests for git_footers."""
from io import StringIO
import json
import os
import sys
import tempfile
import unittest
if sys.version_info.major == 2:
from StringIO import StringIO
import mock
else:
from io import StringIO
from unittest import mock
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

@ -4,22 +4,12 @@
# found in the LICENSE file.
"""Tests for git_dates."""
from __future__ import unicode_literals
import datetime
from io import BytesIO, StringIO
import os
import re
import shutil
import sys
import tempfile
import unittest
from io import BytesIO
if sys.version_info.major == 2:
from StringIO import StringIO
import mock
else:
from io import StringIO
from unittest import mock
DEPOT_TOOLS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -29,7 +19,6 @@ from testing_support import coverage_utils
from testing_support import git_test_utils
import gclient_utils
import git_common
GitRepo = git_test_utils.GitRepo
@ -590,11 +579,7 @@ class GitHyperBlameUnicodeTest(GitHyperBlameTestBase):
# Add a line.
COMMIT_B = {
# AUTHOR_NAME has .encode('utf-8') for py2 as Windows raises exception
# otherwise. Type remains str
GitRepo.AUTHOR_NAME:
('\u4e2d\u56fd\u4f5c\u8005'.encode('utf-8')
if sys.version_info.major == 2 else '\u4e2d\u56fd\u4f5c\u8005'),
GitRepo.AUTHOR_NAME: '\u4e2d\u56fd\u4f5c\u8005',
'file': {
'data': b'red\ngreen\nblue\n'
},
@ -602,40 +587,12 @@ class GitHyperBlameUnicodeTest(GitHyperBlameTestBase):
# Modify a line with non-UTF-8 author and file text.
COMMIT_C = {
GitRepo.AUTHOR_NAME:
('Lat\u00edn-1 Author'.encode('latin-1')
if sys.version_info.major == 2 else 'Lat\xedn-1 Author'),
GitRepo.AUTHOR_NAME: 'Lat\xedn-1 Author',
'file': {
'data': 'red\ngre\u00e9n\nblue\n'.encode('latin-1')
},
}
@unittest.skipIf(
sys.platform.startswith("win") and sys.version_info.major == 2,
"Known issue for Windows and py2")
def testNonASCIIAuthorName(self):
"""Ensures correct tabulation.
Tests the case where there are non-ASCII (UTF-8) characters in the author
name.
Regression test for https://crbug.com/808905.
This test is disabled only for Windows and Python2 as `author` gets escaped
differently.
"""
# Known issue with Windows and py2, skip test for such env
expected_output = [
self.blame_line('A', '1) red', author='ASCII Author'),
# Expect 8 spaces, to line up with the other name.
self.blame_line(
'B', '2) green', author='\u4e2d\u56fd\u4f5c\u8005 '),
self.blame_line('A', '3) blue', author='ASCII Author'),
]
retval, output = self.run_hyperblame([], 'file', 'tag_B')
self.assertEqual(0, retval)
self.assertEqual(expected_output, output)
def testNonUTF8Data(self):
"""Ensures correct behaviour even if author or file data is not UTF-8.

@ -5,14 +5,12 @@
# found in the LICENSE file.
"""Tests for git_map."""
from __future__ import print_function
from __future__ import unicode_literals
import io
import os
import re
import sys
import unittest
from unittest import mock
DEPOT_TOOLS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, DEPOT_TOOLS_ROOT)
@ -22,11 +20,6 @@ from testing_support import git_test_utils
import git_map
import git_common
if sys.version_info.major == 2:
import mock
else:
from unittest import mock
git_common.TEST_MODE = True
GitRepo = git_test_utils.GitRepo

@ -8,10 +8,6 @@ import collections
import os
import sys
import unittest
if sys.version_info.major == 2:
import mock
else:
from unittest import mock
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

@ -5,9 +5,6 @@
"""Unit tests for git_rebase_update.py"""
from __future__ import print_function
from __future__ import unicode_literals
import os
import sys

@ -6,10 +6,6 @@
import os
import sys
import unittest
if sys.version_info.major == 2:
import mock
else:
from unittest import mock
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@ -17,8 +13,6 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import gerrit_util
import owners_client
from testing_support import filesystem_mock
alice = 'alice@example.com'
bob = 'bob@example.com'

@ -8,10 +8,6 @@
import os
import sys
import unittest
if sys.version_info.major == 2:
import mock
else:
from unittest import mock
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

Loading…
Cancel
Save