Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 12 additions & 2 deletions .github/aws/permissions-policy.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EC2ReadAnyRegion",
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:Get*",
"ec2:List*"
],
"Resource": "*"
},
{
"Sid": "EC2FullAccessUsEast1",
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*",
"Condition": {
"StringEqualsIfExists": {
"ec2:Region": "us-east-1"
"StringEquals": {
"aws:RequestedRegion": "us-east-1"
}
}
},
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ jobs:
python-version: 3.13
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip setuptools
python3 -m pip install --upgrade twine wheel
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade build twine
- name: Create and check packages
run: |
python3 setup.py sdist bdist_wheel
python3 -m build
twine check dist/*
ls -l dist
- name: Publish distribution 📦 to Test PyPI
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/integration-cloud.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
uses: actions/cache@v5
with:
path: ~/.cache/pip
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('**/setup.py', '**/requirements.txt') }}
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml', '**/requirements.txt') }}

- name: Install required packages
run: pip install tox
Expand All @@ -100,10 +100,12 @@ jobs:
AZURE_SUBSCRIPTION_ID: ${{ matrix.cloud-provider == 'azure' && secrets.AZURE_SUBSCRIPTION_ID || '' }}
AZURE_SECRET: ${{ matrix.cloud-provider == 'azure' && secrets.AZURE_SECRET || '' }}
AZURE_TENANT: ${{ matrix.cloud-provider == 'azure' && secrets.AZURE_TENANT || '' }}
AZURE_REGION_NAME: ${{ matrix.cloud-provider == 'azure' && secrets.AZURE_REGION_NAME || '' }}
AZURE_RESOURCE_GROUP: ${{ matrix.cloud-provider == 'azure' && secrets.AZURE_RESOURCE_GROUP || '' }}
AZURE_STORAGE_ACCOUNT: ${{ matrix.cloud-provider == 'azure' && secrets.AZURE_STORAGE_ACCOUNT || '' }}
CB_IMAGE_AZURE: ${{ matrix.cloud-provider == 'azure' && secrets.CB_IMAGE_AZURE || '' }}
CB_VM_TYPE_AZURE: ${{ matrix.cloud-provider == 'azure' && secrets.CB_VM_TYPE_AZURE || '' }}
CB_PLACEMENT_AZURE: ${{ matrix.cloud-provider == 'azure' && secrets.CB_PLACEMENT_AZURE || '' }}
# gcp
GCP_SERVICE_CREDS_DICT: ${{ matrix.cloud-provider == 'gcp' && secrets.GCP_SERVICE_CREDS_DICT || '' }}
CB_IMAGE_GCP: ${{ matrix.cloud-provider == 'gcp' && secrets.CB_IMAGE_GCP || '' }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
uses: actions/cache@v5
with:
path: ~/.cache/pip
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('**/setup.py', '**/requirements.txt') }}
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml', '**/requirements.txt') }}

- name: Install required packages
run: pip install tox
Expand Down
12 changes: 10 additions & 2 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
version: 2

build:
os: ubuntu-24.04
tools:
python: "3.13"

sphinx:
configuration: docs/conf.py

python:
install:
- requirements: docs/requirements.txt
install:
- requirements: docs/requirements.txt
46 changes: 23 additions & 23 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,34 @@ Build Status Tests
:target: https://pypistats.org/packages/cloudbridge
:alt: Download stats

.. |aws-py38| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.8_aws.json
:target: https://github.com/CloudVE/cloudbridge/actions/
.. |aws-py313| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.13_aws.json
:target: https://github.com/CloudVE/cloudbridge/actions/

.. |azure-py38| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.8_azure.json
:target: https://github.com/CloudVE/cloudbridge/actions/
.. |azure-py313| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.13_azure.json
:target: https://github.com/CloudVE/cloudbridge/actions/

.. |gcp-py38| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.8_gcp.json
:target: https://github.com/CloudVE/cloudbridge/actions/
.. |gcp-py313| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.13_gcp.json
:target: https://github.com/CloudVE/cloudbridge/actions/

.. |mock-py313| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.13_mock.json
:target: https://github.com/CloudVE/cloudbridge/actions/

.. |mock-py38| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.8_mock.json
.. |os-py313| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.13_openstack.json
:target: https://github.com/CloudVE/cloudbridge/actions/

.. |os-py38| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/nuwang/d354f151eb8c9752da13e6dec012fb07/raw/cloudbridge_py3.8_openstack.json
:target: https://github.com/CloudVE/cloudbridge/actions/

+---------------------------+----------------+
| **Provider/Environment** | **Python 3.8** |
+---------------------------+----------------+
| **Amazon Web Services** | |aws-py38| |
+---------------------------+----------------+
| **Google Cloud Platform** | |gcp-py38| |
+---------------------------+----------------+
| **Microsoft Azure** | |azure-py38| |
+---------------------------+----------------+
| **OpenStack** | |os-py38| |
+---------------------------+----------------+
| **Mock Provider** | |mock-py38| |
+---------------------------+----------------+
+---------------------------+-----------------+
| **Provider/Environment** | **Python 3.13** |
+---------------------------+-----------------+
| **Amazon Web Services** | |aws-py313| |
+---------------------------+-----------------+
| **Google Cloud Platform** | |gcp-py313| |
+---------------------------+-----------------+
| **Microsoft Azure** | |azure-py313| |
+---------------------------+-----------------+
| **OpenStack** | |os-py313| |
+---------------------------+-----------------+
| **Mock Provider** | |mock-py313| |
+---------------------------+-----------------+

Installation
~~~~~~~~~~~~
Expand Down
45 changes: 45 additions & 0 deletions TODO.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
Deferred Modernization Work
===========================

The packaging migration (setup.py → pyproject.toml, drop six, refresh docs)
landed in commits ``7df4a94``..``HEAD``. The items below were identified
during that sweep but deliberately left out because each is large enough
to warrant its own focused PR.

Mechanical Python idiom updates
-------------------------------

Each of these is a near-mechanical refactor with a wide diff. Best done
one-at-a-time so reviewers can read each change as a single transformation.

* **Drop explicit ``object`` base class.** ``class Foo(object):`` →
``class Foo:``. No behavior change in Py3.
* **Modernize ``super()`` calls.** ``super(ClassName, self).method(...)`` →
``super().method(...)``. The arguments are required only in Py2.
* **Adopt f-strings.** ``"x={0}".format(x)`` and ``"x=%s" % x`` →
``f"x={x}"``. Skip for logging calls — those should keep ``%s``
formatting so the logger can short-circuit when the level is disabled.
* **Switch typing imports to builtins.** ``List[X]`` / ``Dict[K, V]`` /
``Optional[X]`` → ``list[X]`` / ``dict[K, V]`` / ``X | None`` once a
Python 3.10+ floor is acceptable (we already require 3.13, so this is
safe today).

Lint and tooling
----------------

* **Fix the ~23 pre-existing flake8 import-order errors.** Run
``tox -e lint`` to see the list. Mostly ``I100``/``I201``/``I202``
under ``cloudbridge/providers/azure``, ``gcp``, and ``openstack``.
* **Consider replacing flake8 + flake8-import-order with ruff.** Ruff
reads ``pyproject.toml``, runs ~100× faster, and covers import
ordering (``I``) plus most flake8 plugins out of the box.
* **Consider adding mypy / pyright in CI.** The codebase has no type
hints today; this would be a meaningful uplift, not a one-PR task.

Repository hygiene
------------------

* **Untracked local-dev artifacts at the repo root** — ``azure.txt``,
``openstack.txt``, ``docs2/``, ``script_test.py``, ``openstack.log``.
Each likely belongs in ``.gitignore`` or in a developer's untracked
workspace; investigate before either committing or deleting.
10 changes: 1 addition & 9 deletions cloudbridge/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ def init_logging():
set_stream_logger(__name__, level=logging.DEBUG)


class NullHandler(logging.Handler):
"""A null handler for the logger."""

def emit(self, record):
"""Don't emit a log."""
pass


TRACE = 5 # Lower than debug which is 10


Expand All @@ -58,7 +50,7 @@ def trace(self, msg, *args, **kwargs):
logging.setLoggerClass(CBLogger)
logging.addLevelName(TRACE, "TRACE")
log = logging.getLogger('cloudbridge')
log.addHandler(NullHandler())
log.addHandler(logging.NullHandler())

# Convenience functions to set logging to a particular file or stream
# To enable either of these by default within CloudBridge, add the following
Expand Down
19 changes: 3 additions & 16 deletions cloudbridge/base/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import logging
import os
import re
import sys
from contextlib import contextmanager

from cryptography.hazmat.backends import default_backend
Expand All @@ -12,8 +11,6 @@

from deprecation import deprecated

import six

import cloudbridge

from ..interfaces.exceptions import InvalidParamException
Expand Down Expand Up @@ -50,7 +47,7 @@ def filter_by(prop_name, kwargs, objs):
"""
prop_val = kwargs.pop(prop_name, None)
if prop_val:
if isinstance(prop_val, six.string_types):
if isinstance(prop_val, str):
regex = fnmatch.translate(prop_val)
results = [o for o in objs
if getattr(o, prop_name)
Expand Down Expand Up @@ -101,12 +98,11 @@ def cleanup_action(cleanup_func):
try:
yield
except Exception:
ex_class, ex_val, ex_traceback = sys.exc_info()
try:
cleanup_func()
except Exception:
log.exception("Error during exception cleanup: ")
six.reraise(ex_class, ex_val, ex_traceback)
raise
try:
cleanup_func()
except Exception:
Expand All @@ -117,11 +113,6 @@ def get_env(varname, default_value=None):
"""
Return the value of the environment variable or default_value.

This is a helper method that wraps ``os.environ.get`` to ensure type
compatibility across py2 and py3. For py2, any value obtained from an
environment variable, ensure ``unicode`` type and ``str`` for py3. The
casting is done only for string variables.

:type varname: ``str``
:param varname: Name of the environment variable for which to check.

Expand All @@ -131,11 +122,7 @@ def get_env(varname, default_value=None):
:return: Value of the supplied environment if found; value of
``default_value`` otherwise.
"""
value = os.environ.get(varname, default_value)
if isinstance(value, six.string_types) and not isinstance(
value, six.text_type):
return six.u(value)
return value
return os.environ.get(varname, default_value)


# Alias deprecation decorator, following:
Expand Down
16 changes: 4 additions & 12 deletions cloudbridge/base/middleware.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import logging
import sys

from pyeventsystem.middleware import dispatch as pyevent_dispatch
from pyeventsystem.middleware import intercept
from pyeventsystem.middleware import observe

import six

from ..interfaces.exceptions import CloudBridgeBaseException

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -46,12 +43,7 @@ def wrap_exception(self, event_args, *args, **kwargs):
except Exception as e:
if isinstance(e, CloudBridgeBaseException):
raise
else:
ex_type, ex_value, traceback = sys.exc_info()
cb_ex = CloudBridgeBaseException(
"CloudBridgeBaseException: {0} from exception type: {1}"
.format(ex_value, ex_type))
if sys.version_info >= (3, 0):
six.raise_from(cb_ex, e)
else:
six.reraise(CloudBridgeBaseException, cb_ex, traceback)
cb_ex = CloudBridgeBaseException(
"CloudBridgeBaseException: {0} from exception type: {1}"
.format(e, type(e)))
raise cb_ex from e
10 changes: 1 addition & 9 deletions cloudbridge/base/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
import functools
import logging
import os
from configparser import ConfigParser
from os.path import expanduser
try:
from configparser import ConfigParser
except ImportError: # Python 2
from ConfigParser import SafeConfigParser as ConfigParser

from pyeventsystem.middleware import SimpleMiddlewareManager

import six

from ..base.middleware import ExceptionWrappingMiddleware
from ..interfaces import CloudProvider
from ..interfaces.exceptions import ProviderConnectionException
Expand Down Expand Up @@ -206,7 +201,4 @@ def _get_config_value(self, key, default_value=None):
elif (self._config_parser.has_option(self.PROVIDER_ID, key) and
self._config_parser.get(self.PROVIDER_ID, key)):
value = self._config_parser.get(self.PROVIDER_ID, key)
if isinstance(value, six.string_types) and not isinstance(
value, six.text_type):
return six.u(value)
return value
4 changes: 1 addition & 3 deletions cloudbridge/base/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import time
import uuid

import six

from cloudbridge.interfaces.exceptions import \
InvalidConfigurationException
from cloudbridge.interfaces.exceptions import InvalidLabelException
Expand Down Expand Up @@ -378,7 +376,7 @@ def _validate_volume_device(self, source=None, is_root=None,
raise InvalidConfigurationException(
"Source must be a Snapshot, Volume, MachineImage, or None.")
if size:
if not isinstance(size, six.integer_types) or not size > 0:
if not isinstance(size, int) or not size > 0:
log.exception("InvalidConfigurationException raised: "
"size argument must be an integer greater than "
"0. Got type %s and value %s.", type(size), size)
Expand Down
Loading
Loading