Remove six and update lower-constraints appdirs

This patch removes the use of "six" as the package declares only
python3 support.
It also updates the appdirs lower-constraint to 1.4.0 to support
pip. This version is available on both centos8 and focal.
It also removes the linter related packages from lower-constraints.

Change-Id: I9337f1998749bc40737f2f0e2dcc406b6f3a0ddf
This commit is contained in:
Michael Johnson 2021-03-25 23:45:23 +00:00
parent 233b1ca4b0
commit 820f1e9af9
17 changed files with 46 additions and 70 deletions

View File

@ -16,17 +16,15 @@
import abc import abc
import six
from six.moves.urllib import parse
from stevedore import extension from stevedore import extension
from urllib import parse
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from designateclient import exceptions from designateclient import exceptions
@six.add_metaclass(abc.ABCMeta) class Controller(object, metaclass=abc.ABCMeta):
class Controller(object):
def __init__(self, client): def __init__(self, client):
self.client = client self.client = client
@ -89,8 +87,7 @@ class Controller(object):
return body return body
@six.add_metaclass(abc.ABCMeta) class CrudController(Controller, metaclass=abc.ABCMeta):
class CrudController(Controller):
@abc.abstractmethod @abc.abstractmethod
def list(self, *args, **kw): def list(self, *args, **kw):
@ -132,6 +129,6 @@ def Client(version, *args, **kwargs): # noqa
versions = get_versions() versions = get_versions()
if version not in versions: if version not in versions:
msg = 'Version %s is not supported, use one of (%s)' % ( msg = 'Version %s is not supported, use one of (%s)' % (
version, list(six.iterkeys(versions))) version, list(versions.keys()))
raise exceptions.UnsupportedVersion(msg) raise exceptions.UnsupportedVersion(msg)
return versions[version](*args, **kwargs) return versions[version](*args, **kwargs)

View File

@ -13,7 +13,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
""" """
import six
from tempest.lib.cli import output_parser from tempest.lib.cli import output_parser
@ -67,7 +66,7 @@ class FieldValueModel(Model):
class ListEntryModel(Model): class ListEntryModel(Model):
def __init__(self, fields, values): def __init__(self, fields, values):
for k, v in six.moves.zip(fields, values): for k, v in zip(fields, values):
setattr(self, k, v) setattr(self, k, v)

View File

@ -20,8 +20,7 @@ from keystoneauth1 import session as keystone_session
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslotest import base as test from oslotest import base as test
from requests_mock.contrib import fixture as req_fixture from requests_mock.contrib import fixture as req_fixture
import six from urllib import parse as urlparse
from six.moves.urllib import parse as urlparse
from designateclient import client from designateclient import client
from designateclient.utils import AdapterWithTimeout from designateclient.utils import AdapterWithTimeout
@ -132,7 +131,7 @@ class APITestCase(TestCase):
parts = urlparse.urlparse(self.requests.last_request.url) parts = urlparse.urlparse(self.requests.last_request.url)
qs = urlparse.parse_qs(parts.query, keep_blank_values=True) qs = urlparse.parse_qs(parts.query, keep_blank_values=True)
for k, v in six.iteritems(kwargs): for k, v in kwargs.items():
self.assertIn(k, qs) self.assertIn(k, qs)
self.assertIn(v, qs[k]) self.assertIn(v, qs[k])

View File

@ -17,7 +17,6 @@
import uuid import uuid
from keystoneauth1 import adapter from keystoneauth1 import adapter
import six
from designateclient import exceptions from designateclient import exceptions
@ -64,8 +63,7 @@ def get_columns(data):
def _seen(col): def _seen(col):
columns.add(str(col)) columns.add(str(col))
six.moves.map(lambda item: six.moves.map(_seen, map(lambda item: map(_seen, list(item.keys())), data)
list(six.iterkeys(item))), data)
return list(columns) return list(columns)

View File

@ -17,7 +17,6 @@
import logging import logging
from osc_lib.command import command from osc_lib.command import command
import six
from designateclient import utils from designateclient import utils
from designateclient.v2.cli import common from designateclient.v2.cli import common
@ -70,7 +69,7 @@ class ShowBlacklistCommand(command.ShowOne):
common.set_all_common_headers(client, parsed_args) common.set_all_common_headers(client, parsed_args)
data = client.blacklists.get(parsed_args.id) data = client.blacklists.get(parsed_args.id)
_format_blacklist(data) _format_blacklist(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class CreateBlacklistCommand(command.ShowOne): class CreateBlacklistCommand(command.ShowOne):
@ -95,7 +94,7 @@ class CreateBlacklistCommand(command.ShowOne):
parsed_args.pattern, parsed_args.description) parsed_args.pattern, parsed_args.description)
_format_blacklist(data) _format_blacklist(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class SetBlacklistCommand(command.ShowOne): class SetBlacklistCommand(command.ShowOne):
@ -132,7 +131,7 @@ class SetBlacklistCommand(command.ShowOne):
updated = client.blacklists.update(parsed_args.id, data) updated = client.blacklists.update(parsed_args.id, data)
_format_blacklist(updated) _format_blacklist(updated)
return six.moves.zip(*sorted(six.iteritems(updated))) return zip(*sorted(updated.items()))
class DeleteBlacklistCommand(command.Command): class DeleteBlacklistCommand(command.Command):

View File

@ -13,7 +13,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import six
def add_all_projects_option(parser): def add_all_projects_option(parser):
@ -71,5 +70,5 @@ def set_all_common_headers(client, parsed_args):
set_edit_managed(client, parsed_args.edit_managed) set_edit_managed(client, parsed_args.edit_managed)
if parsed_args.sudo_project_id is not None and \ if parsed_args.sudo_project_id is not None and \
isinstance(parsed_args.sudo_project_id, six.string_types): isinstance(parsed_args.sudo_project_id, str):
set_sudo_project_id(client, parsed_args.sudo_project_id) set_sudo_project_id(client, parsed_args.sudo_project_id)

View File

@ -18,7 +18,6 @@ import logging
from cliff import command from cliff import command
from cliff import show from cliff import show
import six
from designateclient.v2.cli import common from designateclient.v2.cli import common
@ -60,7 +59,7 @@ class ListQuotasCommand(show.ShowOne):
common.set_all_projects(client, True) common.set_all_projects(client, True)
data = client.quotas.list(proj_id) data = client.quotas.list(proj_id)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class SetQuotasCommand(show.ShowOne): class SetQuotasCommand(show.ShowOne):
@ -104,7 +103,7 @@ class SetQuotasCommand(show.ShowOne):
updated = client.quotas.update(proj_id, quotas) updated = client.quotas.update(proj_id, quotas)
return six.moves.zip(*sorted(six.iteritems(updated))) return zip(*sorted(updated.items()))
class ResetQuotasCommand(command.Command): class ResetQuotasCommand(command.Command):

View File

@ -18,7 +18,6 @@ import argparse
import logging import logging
from osc_lib.command import command from osc_lib.command import command
import six
from designateclient import utils from designateclient import utils
from designateclient.v2.cli import common from designateclient.v2.cli import common
@ -135,7 +134,7 @@ class ShowRecordSetCommand(command.ShowOne):
data = client.recordsets.get(parsed_args.zone_id, parsed_args.id) data = client.recordsets.get(parsed_args.zone_id, parsed_args.id)
_format_recordset(data) _format_recordset(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class CreateRecordSetCommand(command.ShowOne): class CreateRecordSetCommand(command.ShowOne):
@ -182,7 +181,7 @@ class CreateRecordSetCommand(command.ShowOne):
ttl=parsed_args.ttl) ttl=parsed_args.ttl)
_format_recordset(data) _format_recordset(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class SetRecordSetCommand(command.ShowOne): class SetRecordSetCommand(command.ShowOne):
@ -246,7 +245,7 @@ class SetRecordSetCommand(command.ShowOne):
_format_recordset(updated) _format_recordset(updated)
return six.moves.zip(*sorted(six.iteritems(updated))) return zip(*sorted(updated.items()))
class DeleteRecordSetCommand(command.ShowOne): class DeleteRecordSetCommand(command.ShowOne):
@ -270,4 +269,4 @@ class DeleteRecordSetCommand(command.ShowOne):
LOG.info('RecordSet %s was deleted', parsed_args.id) LOG.info('RecordSet %s was deleted', parsed_args.id)
_format_recordset(data) _format_recordset(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))

View File

@ -17,7 +17,6 @@
import logging import logging
from osc_lib.command import command from osc_lib.command import command
import six
from designateclient import utils from designateclient import utils
from designateclient.v2.cli import common from designateclient.v2.cli import common
@ -71,7 +70,7 @@ class ShowFloatingIPCommand(command.ShowOne):
common.set_all_common_headers(client, parsed_args) common.set_all_common_headers(client, parsed_args)
data = client.floatingips.get(parsed_args.floatingip_id) data = client.floatingips.get(parsed_args.floatingip_id)
_format_floatingip(data) _format_floatingip(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class SetFloatingIPCommand(command.ShowOne): class SetFloatingIPCommand(command.ShowOne):
@ -119,7 +118,7 @@ class SetFloatingIPCommand(command.ShowOne):
parsed_args.ttl) parsed_args.ttl)
_format_floatingip(fip) _format_floatingip(fip)
return six.moves.zip(*sorted(six.iteritems(fip))) return zip(*sorted(fip.items()))
class UnsetFloatingIPCommand(command.Command): class UnsetFloatingIPCommand(command.Command):

View File

@ -17,7 +17,6 @@
import logging import logging
from osc_lib.command import command from osc_lib.command import command
import six
from designateclient import utils from designateclient import utils
from designateclient.v2.cli import common from designateclient.v2.cli import common
@ -91,4 +90,4 @@ class ShowServiceStatusCommand(command.ShowOne):
data = client.service_statuses.get(parsed_args.id) data = client.service_statuses.get(parsed_args.id)
_format_status(data) _format_status(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))

View File

@ -17,7 +17,6 @@
import logging import logging
from osc_lib.command import command from osc_lib.command import command
import six
from designateclient import utils from designateclient import utils
from designateclient.v2.cli import common from designateclient.v2.cli import common
@ -75,7 +74,7 @@ class ShowTLDCommand(command.ShowOne):
common.set_all_common_headers(client, parsed_args) common.set_all_common_headers(client, parsed_args)
data = client.tlds.get(parsed_args.id) data = client.tlds.get(parsed_args.id)
_format_tld(data) _format_tld(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class CreateTLDCommand(command.ShowOne): class CreateTLDCommand(command.ShowOne):
@ -96,7 +95,7 @@ class CreateTLDCommand(command.ShowOne):
common.set_all_common_headers(client, parsed_args) common.set_all_common_headers(client, parsed_args)
data = client.tlds.create(parsed_args.name, parsed_args.description) data = client.tlds.create(parsed_args.name, parsed_args.description)
_format_tld(data) _format_tld(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class SetTLDCommand(command.ShowOne): class SetTLDCommand(command.ShowOne):
@ -131,7 +130,7 @@ class SetTLDCommand(command.ShowOne):
data = client.tlds.update(parsed_args.id, data) data = client.tlds.update(parsed_args.id, data)
_format_tld(data) _format_tld(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class DeleteTLDCommand(command.Command): class DeleteTLDCommand(command.Command):

View File

@ -17,7 +17,6 @@
import logging import logging
from osc_lib.command import command from osc_lib.command import command
import six
from designateclient import utils from designateclient import utils
from designateclient.v2.cli import common from designateclient.v2.cli import common
@ -83,7 +82,7 @@ class ShowTSIGKeyCommand(command.ShowOne):
common.set_all_common_headers(client, parsed_args) common.set_all_common_headers(client, parsed_args)
data = client.tsigkeys.get(parsed_args.id) data = client.tsigkeys.get(parsed_args.id)
_format_tsigkey(data) _format_tsigkey(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class CreateTSIGKeyCommand(command.ShowOne): class CreateTSIGKeyCommand(command.ShowOne):
@ -111,7 +110,7 @@ class CreateTSIGKeyCommand(command.ShowOne):
parsed_args.secret, parsed_args.scope, parsed_args.secret, parsed_args.scope,
parsed_args.resource_id) parsed_args.resource_id)
_format_tsigkey(data) _format_tsigkey(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class SetTSIGKeyCommand(command.ShowOne): class SetTSIGKeyCommand(command.ShowOne):
@ -147,7 +146,7 @@ class SetTSIGKeyCommand(command.ShowOne):
data = client.tsigkeys.update(parsed_args.id, data) data = client.tsigkeys.update(parsed_args.id, data)
_format_tsigkey(data) _format_tsigkey(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class DeleteTSIGKeyCommand(command.Command): class DeleteTSIGKeyCommand(command.Command):

View File

@ -18,7 +18,6 @@ import logging
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions as osc_exc from osc_lib import exceptions as osc_exc
import six
from designateclient import utils from designateclient import utils
from designateclient.v2.cli import common from designateclient.v2.cli import common
@ -121,7 +120,7 @@ class ShowZoneCommand(command.ShowOne):
data = client.zones.get(parsed_args.id) data = client.zones.get(parsed_args.id)
_format_zone(data) _format_zone(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class CreateZoneCommand(command.ShowOne): class CreateZoneCommand(command.ShowOne):
@ -186,7 +185,7 @@ class CreateZoneCommand(command.ShowOne):
parsed_args.name, parsed_args.type, **payload) parsed_args.name, parsed_args.type, **payload)
_format_zone(data) _format_zone(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class SetZoneCommand(command.ShowOne): class SetZoneCommand(command.ShowOne):
@ -231,7 +230,7 @@ class SetZoneCommand(command.ShowOne):
updated = client.zones.update(parsed_args.id, data) updated = client.zones.update(parsed_args.id, data)
_format_zone(updated) _format_zone(updated)
return six.moves.zip(*sorted(six.iteritems(updated))) return zip(*sorted(updated.items()))
class DeleteZoneCommand(command.ShowOne): class DeleteZoneCommand(command.ShowOne):
@ -254,7 +253,7 @@ class DeleteZoneCommand(command.ShowOne):
LOG.info('Zone %s was deleted', parsed_args.id) LOG.info('Zone %s was deleted', parsed_args.id)
_format_zone(data) _format_zone(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class AbandonZoneCommand(command.Command): class AbandonZoneCommand(command.Command):
@ -323,7 +322,7 @@ class CreateTransferRequestCommand(command.ShowOne):
data = client.zone_transfers.create_request( data = client.zone_transfers.create_request(
parsed_args.zone_id, parsed_args.target_project_id, parsed_args.zone_id, parsed_args.target_project_id,
parsed_args.description) parsed_args.description)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class ListTransferRequestsCommand(command.Lister): class ListTransferRequestsCommand(command.Lister):
@ -368,7 +367,7 @@ class ShowTransferRequestCommand(command.ShowOne):
data = client.zone_transfers.get_request(parsed_args.id) data = client.zone_transfers.get_request(parsed_args.id)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class SetTransferRequestCommand(command.ShowOne): class SetTransferRequestCommand(command.ShowOne):
@ -398,7 +397,7 @@ class SetTransferRequestCommand(command.ShowOne):
data['description'] = parsed_args.description data['description'] = parsed_args.description
updated = client.zone_transfers.update_request(parsed_args.id, data) updated = client.zone_transfers.update_request(parsed_args.id, data)
return six.moves.zip(*sorted(six.iteritems(updated))) return zip(*sorted(updated.items()))
class DeleteTransferRequestCommand(command.Command): class DeleteTransferRequestCommand(command.Command):
@ -444,7 +443,7 @@ class AcceptTransferRequestCommand(command.ShowOne):
data = client.zone_transfers.accept_request( data = client.zone_transfers.accept_request(
parsed_args.transfer_id, parsed_args.key) parsed_args.transfer_id, parsed_args.key)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class ListTransferAcceptsCommand(command.Lister): class ListTransferAcceptsCommand(command.Lister):
@ -489,7 +488,7 @@ class ShowTransferAcceptCommand(command.ShowOne):
data = client.zone_transfers.get_accept(parsed_args.id) data = client.zone_transfers.get_accept(parsed_args.id)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class ExportZoneCommand(command.ShowOne): class ExportZoneCommand(command.ShowOne):
@ -514,7 +513,7 @@ class ExportZoneCommand(command.ShowOne):
LOG.info('Zone Export %s was created', data['id']) LOG.info('Zone Export %s was created', data['id'])
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class ListZoneExportsCommand(command.Lister): class ListZoneExportsCommand(command.Lister):
@ -567,7 +566,7 @@ class ShowZoneExportCommand(command.ShowOne):
parsed_args.zone_export_id) parsed_args.zone_export_id)
_format_zone_export_record(data) _format_zone_export_record(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class DeleteZoneExportCommand(command.Command): class DeleteZoneExportCommand(command.Command):
@ -640,7 +639,7 @@ class ImportZoneCommand(command.ShowOne):
LOG.info('Zone Import %s was created', data['id']) LOG.info('Zone Import %s was created', data['id'])
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class ListZoneImportsCommand(command.Lister): class ListZoneImportsCommand(command.Lister):
@ -694,7 +693,7 @@ class ShowZoneImportCommand(command.ShowOne):
parsed_args.zone_import_id) parsed_args.zone_import_id)
_format_zone_import_record(data) _format_zone_import_record(data)
return six.moves.zip(*sorted(six.iteritems(data))) return zip(*sorted(data.items()))
class DeleteZoneImportCommand(command.Command): class DeleteZoneImportCommand(command.Command):

View File

@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_utils import uuidutils from oslo_utils import uuidutils
import six
from designateclient.v2.base import V2Controller from designateclient.v2.base import V2Controller
from designateclient.v2 import utils as v2_utils from designateclient.v2 import utils as v2_utils
@ -25,7 +24,7 @@ class RecordSetController(V2Controller):
zone_info = None zone_info = None
# If we get a zone name we'll need to get the ID of it before POST. # If we get a zone name we'll need to get the ID of it before POST.
if isinstance(zone, six.string_types) and not \ if isinstance(zone, str) and not \
uuidutils.is_uuid_like(zone): uuidutils.is_uuid_like(zone):
zone_info = self.client.zones.get(zone) zone_info = self.client.zones.get(zone)
elif isinstance(zone, dict): elif isinstance(zone, dict):

View File

@ -15,10 +15,8 @@
# under the License. # under the License.
from oslo_utils import uuidutils from oslo_utils import uuidutils
from six import iteritems from urllib.parse import parse_qs
from six import iterkeys from urllib.parse import urlparse
from six.moves.urllib.parse import parse_qs
from six.moves.urllib.parse import urlparse
from designateclient import exceptions from designateclient import exceptions
@ -51,7 +49,7 @@ def parse_query_from_url(url):
:return: dict :return: dict
""" """
values = parse_qs(urlparse(url)[4]) values = parse_qs(urlparse(url)[4])
return {k: values[k][0] for k in iterkeys(values)} return {k: values[k][0] for k in values.keys()}
def get_all(function, criterion=None, args=None): def get_all(function, criterion=None, args=None):
@ -70,7 +68,7 @@ def get_all(function, criterion=None, args=None):
returned_data = data returned_data = data
while True: while True:
if data.next_page: if data.next_page:
for k, v in iteritems(data.next_link_criterion): for k, v in data.next_link_criterion.items():
criterion[k] = v criterion[k] = v
data = function(*args, criterion=criterion) data = function(*args, criterion=criterion)
returned_data.extend(data) returned_data.extend(data)

View File

@ -1,4 +1,4 @@
appdirs==1.3.0 appdirs==1.4.0
asn1crypto==0.23.0 asn1crypto==0.23.0
Babel==2.3.4 Babel==2.3.4
cffi==1.14.0 cffi==1.14.0
@ -23,7 +23,6 @@ jsonpointer==1.13
jsonschema==2.6.0 jsonschema==2.6.0
keystoneauth1==3.4.0 keystoneauth1==3.4.0
linecache2==1.0.0 linecache2==1.0.0
mccabe==0.6.0
monotonic==0.6 monotonic==0.6
mox3==0.20.0 mox3==0.20.0
msgpack-python==0.4.0 msgpack-python==0.4.0
@ -45,11 +44,9 @@ oslo.utils==3.33.0
oslotest==3.2.0 oslotest==3.2.0
paramiko==2.0.0 paramiko==2.0.0
pbr==2.0.0 pbr==2.0.0
pep8==1.5.7
prettytable==0.7.2 prettytable==0.7.2
pyasn1==0.1.8 pyasn1==0.1.8
pycparser==2.18 pycparser==2.18
pyflakes==2.1.0
pyinotify==0.9.6 pyinotify==0.9.6
pyparsing==2.1.0 pyparsing==2.1.0
pyperclip==1.5.27 pyperclip==1.5.27
@ -64,7 +61,6 @@ requests-mock==1.2.0
requestsexceptions==1.2.0 requestsexceptions==1.2.0
rfc3986==0.3.1 rfc3986==0.3.1
simplejson==3.5.1 simplejson==3.5.1
six==1.10.0
stevedore==1.20.0 stevedore==1.20.0
tempest==17.1.0 tempest==17.1.0
stestr==2.0.0 stestr==2.0.0

View File

@ -9,6 +9,5 @@ oslo.utils>=3.33.0 # Apache-2.0
pbr!=2.1.0,>=2.0.0 # Apache-2.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0
keystoneauth1>=3.4.0 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0
requests>=2.14.2 # Apache-2.0 requests>=2.14.2 # Apache-2.0
six>=1.10.0 # MIT
stevedore>=1.20.0 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0
debtcollector>=1.2.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0