Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6c07930
Python 3.8 is EOL, remove it from most places in the build pipelines and
Kami Mar 2, 2025
9604a03
Add Python 3.13 the build pipeline and tox config.
Kami Mar 2, 2025
6301c9b
Upgrade cryptography.
Kami Mar 2, 2025
7380215
Try fixing failing integration test.
Kami Mar 2, 2025
e650840
Update time import script to use Python 3.9.
Kami Mar 2, 2025
78d3a29
Add nosec pragma for bandit false positives. Also re-format some of the
Kami Mar 2, 2025
36bfcaf
Remove Python 3.7 and 3.8 from test Docker image, add pypy 3.10, 3.12…
Kami Mar 2, 2025
96d00b8
Also run tests under PyPy 3.10 on CI.
Kami Mar 2, 2025
891fd29
Add missing entry (environment) to tox.ini.
Kami Mar 2, 2025
43c4b84
Upgrade rstcheck and mypy dev / testing / docs dependency.
Kami Mar 2, 2025
b70f725
Upgrade pylint, astroid, black and isort.
Kami Mar 2, 2025
0d748c8
Reformat code with the latest version of black.
Kami Mar 2, 2025
94c989a
Remove Python 3.8 from classifiers, update CHANGELOG.rst to indicate
Kami Mar 2, 2025
6f3edb7
Fix possible bug / edge case detected by the latest version of pylint.
Kami Mar 2, 2025
d8e154f
Update pyupgrade to use Python >= 3.9 syntax and functionality.
Kami Mar 2, 2025
3783d7f
Update black config.
Kami Mar 2, 2025
9e07d92
Update upgrade notes.
Kami Mar 2, 2025
cc85efe
Fix various bugs detected by the new version of pylint.
Kami Mar 2, 2025
27b1f35
Update required status checks.
Kami Mar 2, 2025
2d18970
Fix more bugs detected by the new version of pylint.
Kami Mar 2, 2025
0148618
Reformat code with black.
Kami Mar 2, 2025
cb85b28
Merge branch 'trunk' of github.com:apache/libcloud into chore/remove/…
Kami Mar 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .asf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ github:
strict: true
# contexts are the names of checks that must pass
contexts:
- "Unit Tests (Python 3.9)"
- "Unit Tests (Python 3.10)"
- "Unit Tests (Python 3.11)"
- "Unit Tests (Python 3.12)"
- "Unit Tests (Python 3.13)"
- "Run Various Lint and Other Checks (3.9)"
- "Build and upload Documentation (3.9)"
- "Dependency Review"

notifications:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/install_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
- "3.10"
- "3.11"
- "pypy-3.7"
- "pypy-3.8"
- "pypy-3.9"
include:
# python 3.6 is not supported with ubuntu-latest anymore so we need to
# use ubuntu 20.04
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:

strategy:
matrix:
python_version: [3.8]
python_version: [3.9]

steps:
- uses: actions/checkout@master
Expand Down
22 changes: 11 additions & 11 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ jobs:
fail-fast: false
matrix:
python_version:
- 3.8
- 3.9
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
# cryptography is not compatible with older PyPy versions
- "pypy-3.8"
- "pypy-3.9"
- "pypy-3.10"
os:
- ubuntu-latest

Expand Down Expand Up @@ -74,8 +75,7 @@ jobs:
tox -e py${{ matrix.python_version }}

- name: Run dist install checks tox target
# NOTE: 3.12 will be failing until we migrate away from setup.py
if: ${{ matrix.python_version != 'pypy-3.7' && matrix.python_version != 'pypy-3.8' && matrix.python_version != '3.12-dev' }}
if: ${{ matrix.python_version != 'pypy-3.9' && matrix.python_version != 'pypy-3.10' }}
run: |
tox -e py${{ matrix.python_version }}-dist,py${{ matrix.python_version }}-dist-wheel

Expand All @@ -85,7 +85,7 @@ jobs:

strategy:
matrix:
python_version: [3.8]
python_version: [3.9]

steps:
- uses: actions/checkout@master
Expand Down Expand Up @@ -133,7 +133,7 @@ jobs:

strategy:
matrix:
python_version: [3.8]
python_version: [3.9]

steps:
- uses: actions/checkout@master
Expand Down Expand Up @@ -176,7 +176,7 @@ jobs:

strategy:
matrix:
python_version: [3.8]
python_version: [3.9]

steps:
- uses: actions/checkout@master
Expand All @@ -200,7 +200,7 @@ jobs:

strategy:
matrix:
python_version: [3.8]
python_version: [3.9]

steps:
- uses: actions/checkout@master
Expand Down Expand Up @@ -268,7 +268,7 @@ jobs:

strategy:
matrix:
python_version: [3.8]
python_version: [3.9]

steps:
- uses: actions/checkout@master
Expand Down Expand Up @@ -307,7 +307,7 @@ jobs:

strategy:
matrix:
python_version: [3.8]
python_version: [3.9]

steps:
- name: Print Environment Info
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_dev_artifact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Use Python ${{ matrix.python_version }}
uses: actions/setup-python@v5
with:
python-version: 3.8
python-version: 3.9

- name: Install Dependencies
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_pricing_to_s3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

strategy:
matrix:
python_version: [3.8]
python_version: [3.9]

steps:
- name: Print Environment Info
Expand Down
11 changes: 11 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ Changes in Apache Libcloud 3.9.0
Common
~~~~~~

- Indicate we also support Python 3.12 (non beta) and Python 3.13.
(#2050)
[Tomaz Muraus - @Kami]

- Support for Python 3.8 which is EOL has been removed.

If you still want to use Libcloud with Python 3.8, you should use an older
release which still supports Python 3.8.
(#2050)
[Tomaz Muraus - @Kami]

- Support for Python 3.7 which is EOL has been removed.

If you still want to use Libcloud with Python 3.7, you should use an older
Expand Down
10 changes: 5 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ through a unified and easy to use API.
:Issues: https://issues.apache.org/jira/projects/LIBCLOUD/issues
:Website: https://libcloud.apache.org/
:Documentation: https://libcloud.readthedocs.io
:Supported Python Versions: Python >= 3.8, PyPy >= 3.8, Python 3.10 + Pyjion
:Supported Python Versions: Python >= 3.9, PyPy >= 3.9, Python 3.10 + Pyjion
(Python 2.7 and Python 3.4 is supported by the
v2.8.x release series, last version which supports
Python 3.5 is v3.4.0, v3.6.x for Python 3.6, and
v3.8.x for Python 3.7)
v3.8.x for Python 3.7 and 3.8)

Resources you can manage with Libcloud are divided into the following categories:

Expand All @@ -86,10 +86,10 @@ Documentation can be found at <https://libcloud.readthedocs.org>.
Note on Python Version Compatibility
====================================

Libcloud supports Python >= 3.8 and PyPy >= 3.8.
Libcloud supports Python >= 3.9 and PyPy >= 3.9.

* Support for Python 3.7 has been dropped in v3.9.0 release.
Last release series which supports Python 3.6 is v3.6.x.
* Support for Python 3.7 and 3.8 has been dropped in v3.9.0 release.
Last release series which supports Python 3.7 and 3.8 is v3.8.x.
* Support for Python 3.6 has been dropped in v3.7.0 release.
Last release series which supports Python 3.6 is v3.6.x.
* Support for Python 3.5 has been dropped in v3.5.0 release.
Expand Down
20 changes: 9 additions & 11 deletions contrib/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,27 @@ RUN set -e && \
add-apt-repository ppa:pypy/ppa && \
apt-get update && \
apt-get -y install \
python3.8 \
python3.9 \
python3.10 \
python3.11 \
python3.12 \
python3.13 \
python3-dev \
python3.8-dev \
python3.9-dev \
python3.10-dev \
python3.11-dev \
python3.8-distutils \
python3.9-distutils \
python3.12-dev \
python3.13-dev \
# Uses 3.10
pypy3 \
pypy3-dev \
python3-pip \
python3-distutils \
python3.9-distutils \
libvirt-dev \
# Needed by libvirt driver
pkg-config \
# Needed by cryptography library for pypy
libssl-dev
pkg-config

# Workaround for zipp import error issue - https://github.com/pypa/virtualenv/issues/1630
RUN python3.8 -m pip install --upgrade pip

COPY . /libcloud

Expand All @@ -60,6 +58,6 @@ RUN if [ ! -f "/libcloud/README.rst" ]; then echo "libcloud/README.rst file not
WORKDIR /libcloud

RUN set -e && \
python3.8 -m pip install --no-cache-dir -r requirements-ci.txt
python3.9 -m pip install --no-cache-dir -r requirements-ci.txt

CMD ["tox", "-e", "lint,isort-check,black-check,bandit,py3.7,py3.8,py3.9,py3.10,py3.11,pypypy3.8"]
CMD ["tox", "-e", "lint,isort-check,black-check,bandit,py3.9,py3.10,py3.11,py3.12,py3.13,pypypy3.10"]
6 changes: 6 additions & 0 deletions contrib/generate_contributor_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,14 @@ def convert_to_markdown(contributors_map, include_tickets=False):
def compare(item1, item2):
lastname1 = item1.split(" ")[-1].lower()
lastname2 = item2.split(" ")[-1].lower()

return (lastname1 > lastname2) - (lastname1 < lastname2)

names = contributors_map.keys()
names = sorted(names, cmp=compare)

result = []

for name in names:
tickets = contributors_map[name]

Expand All @@ -125,6 +127,7 @@ def compare(item1, item2):
for ticket in tickets:
if "-" not in ticket:
# Invalid ticket number

continue

number = ticket.split("-")[1]
Expand All @@ -133,6 +136,8 @@ def compare(item1, item2):
url = JIRA_URL % (number)
elif ticket.startswith("GITHUB-") or ticket.startswith("GH-"):
url = GITHUB_URL % (number)
else:
url = None

values = {"ticket": ticket, "url": url}
tickets_string.append("[%(ticket)s](%(url)s)" % values)
Expand All @@ -147,6 +152,7 @@ def compare(item1, item2):
result.append(line.strip())

result = "\n".join(result)

return result


Expand Down
7 changes: 3 additions & 4 deletions docs/upgrade_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ preserve the old behavior when this is possible.
Libcloud 3.9.0
--------------

* Support for Python 3.7 which has been EOL for more than a year now has been
removed.
* Support for Python 3.7 and 3.8 which have been EOL has been removed.

If you still want to use Libcloud with Python 3.7, you should use an older
release which still supports Python 3.7.
If you still want to use Libcloud with Python 3.7 or 3.8, you should use an older
release which still supports Python 3.7 and 3.8.

* [AZURE ARM] Added a new argument to destroy_node() to also delete node's managed
OS disk as part of the node's deletion. Defaults to true. This can be reverted by
Expand Down
3 changes: 2 additions & 1 deletion integration/storage/test_minio.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class MinioTest(Integration.ContainerTestBase):
# Output seemed to have changed recently, see
# https://github.com/apache/libcloud/runs/7481114211?check_suite_focus=true
# ready_message = b'Console endpoint is listening on a dynamic port'
ready_message = b"1 Online"
# ready_message = b"1 Online"
ready_message = b"MinIO Object Storage Server"

def test_cdn_url(self):
self.skipTest("Not implemented in driver")
Expand Down
8 changes: 7 additions & 1 deletion libcloud/common/gandi.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ class GandiException(Exception):

def __str__(self):
# pylint: disable=unsubscriptable-object

return "({}) {}".format(self.args[0], self.args[1])

def __repr__(self):
# pylint: disable=unsubscriptable-object

return '<GandiException code {} "{}">'.format(self.args[0], self.args[1])


Expand Down Expand Up @@ -83,6 +85,7 @@ def __init__(

def request(self, method, *args):
args = (self.key,) + args

return super().request(method, *args)


Expand All @@ -106,6 +109,7 @@ def _wait_operation(self, id, timeout=DEFAULT_TIMEOUT, check_interval=DEFAULT_IN

if op["step"] == "DONE":
return True

if op["step"] in ["ERROR", "CANCEL"]:
return False
except (KeyError, IndexError):
Expand All @@ -114,6 +118,7 @@ def _wait_operation(self, id, timeout=DEFAULT_TIMEOUT, check_interval=DEFAULT_IN
raise GandiException(1002, e)

time.sleep(check_interval)

return False


Expand Down Expand Up @@ -147,7 +152,8 @@ def get_uuid(self):
same UUID!
"""
hashstring = "{}:{}:{}".format(self.uuid_prefix, self.id, self.driver.type)
return hashlib.sha1(b(hashstring)).hexdigest()

return hashlib.sha1(b(hashstring)).hexdigest() # nosec


class IPAddress(BaseObject):
Expand Down
14 changes: 12 additions & 2 deletions libcloud/common/nfsn.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,13 @@ def parse_error(self):
# If we only have one of "error" or "debug", use the one that we have.
# If we have both, use both, with a space character in between them.
value = "No message specified"

if error is not None:
value = error

if debug is not None:
value = debug

if error is not None and value is not None:
value = error + " " + value
value = value + " (HTTP Code: %d)" % self.status
Expand All @@ -70,22 +73,25 @@ def _header(self, action, data):
salt = self._salt()
api_key = self.key
data = urlencode(data)
data_hash = hashlib.sha1(data.encode("utf-8")).hexdigest()
data_hash = hashlib.sha1(data.encode("utf-8")).hexdigest() # nosec

string = ";".join((login, timestamp, salt, api_key, action, data_hash))
string_hash = hashlib.sha1(string.encode("utf-8")).hexdigest()
string_hash = hashlib.sha1(string.encode("utf-8")).hexdigest() # nosec

return ";".join((login, timestamp, salt, string_hash))

def request(self, action, params=None, data="", headers=None, method="GET"):
"""Add the X-NFSN-Authentication header to an HTTP request."""

if not headers:
headers = {}

if not params:
params = {}
header = self._header(action, data)

headers["X-NFSN-Authentication"] = header

if method == "POST":
headers["Content-Type"] = "application/x-www-form-urlencoded"

Expand All @@ -94,16 +100,20 @@ def request(self, action, params=None, data="", headers=None, method="GET"):
def encode_data(self, data):
"""NFSN expects the body to be regular key-value pairs that are not
JSON-encoded."""

if data:
data = urlencode(data)

return data

def _salt(self):
"""Return a 16-character alphanumeric string."""
r = random.SystemRandom()

return "".join(r.choice(SALT_CHARACTERS) for _ in range(16))

def _timestamp(self):
"""Return the current number of seconds since the Unix epoch,
as a string."""

return str(int(time.time()))
Loading
Loading