diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..aabd16e --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +name: Deployment +on: + push: + branches-ignore: ["*"] + tags: ["*"] +jobs: + build: + name: Build distribution + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install pypa/build + run: python3 -m pip install build --user + + - name: Build a binary wheel and a source tarball + run: python3 -m build + + - name: Store the distribution packages + uses: actions/upload-artifact@v3 + with: + name: python-package-distributions + path: dist/ + + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + publish-to-pypi: + name: Publish Python distribution to PyPI + if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes + needs: + - build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/rabbitpy + permissions: + id-token: write + steps: + - name: Download all the dists + uses: actions/download-artifact@v4.1.7 + with: + name: python-package-distributions + path: dist/ + + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..316019f --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,46 @@ +name: Testing +on: + push: + branches: ["*"] + paths-ignore: + - 'docs/**' + - '*.rst' + tags-ignore: ["*"] +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 5 + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + env: + TEST_HOST: docker + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install testing dependencies + run: pip install -e '.[dev]' + + - name: Bootstrap + run: ./bootstrap.sh + + - name: Run unit tests + run: coverage run + + - name: Generate coverage report + run: coverage report && coverage xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + file: ./build/coverage.xml + flags: unittests + name: codecov-umbrella + token: ${{secrets.CODECOV_TOKEN}} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c1217d3..0000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -dist: xenial -language: python -python: - - 2.7 - - 3.4 - - 3.5 - - 3.6 - - 3.7 -services: - - docker -install: - - pip install --upgrade -r test-requirements.txt -before_script: - - ./bootstrap.sh -script: nosetests -after_success: - - codecov - - CODECLIMATE_REPO_TOKEN=ed39a2c039a29b0965bfdd243fea7caba2a83a4945bbd69fe95f506066754bd1 codeclimate-test-reporter -deploy: - distributions: sdist bdist_wheel - provider: pypi - user: crad - on: - python: 3.7 - tags: true - all_branches: true - password: - secure: "ls9ja4J6bcirOvWVCvPAWboaOK6O0Pr/aZmHiifUYKEBqUg8D9PB7JyeXybMthXSx+jkHrFp6pL/IjYGrMYQ3gomZ+fsQTavNLxZxEHyLxDf7LVBxQ0KMNNh+GyF7qi9U749MlZBf9JgBFbAgnwCZRnlzCM8j7PxcFevYv4gouE=" diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index c6ebf1f..0000000 --- a/Vagrantfile +++ /dev/null @@ -1,34 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! -VAGRANTFILE_API_VERSION = "2" - -$install = < /etc/apt/sources.list.d/rabbitmq.list -apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv D208507CA14F4FCA -echo "deb http://packages.erlang-solutions.com/debian precise contrib" > /etc/apt/sources.list.d/erlang-solutions.list - -apt-get -q update -apt-get -y -q install python-pip rabbitmq-server python3 python3-pip -apt-get -q clean -echo "[{rabbit, [{loopback_users, []}]}]." > /etc/rabbitmq/rabbitmq.config -service rabbitmq-server restart -rabbitmq-plugins enable rabbitmq_management - -pip install pamqp -pip3 install pamqp -INSTALL - -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - config.vm.box = "Ubuntu" - config.vm.provision "shell", inline: $install - config.vm.synced_folder ".", "/home/vagrant/src" - config.berkshelf.enabled = false - if Vagrant.has_plugin?("vagrant-vbguest") then - config.vbguest.auto_update = false - end - config.vm.network :forwarded_port, host: 5672, guest: 5672 - config.vm.network :forwarded_port, host: 15672, guest: 15672 -end diff --git a/bootstrap.sh b/bootstrap.sh index 2e41e51..13dad96 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # # NAME # bootstrap -- initialize/update docker environment @@ -6,28 +6,30 @@ # vim: set ts=2 sts=2 sw=2 et: set -e -# Common constants -COLOR_RESET='\033[0m' -COLOR_GREEN='\033[0;32m' -COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-${PWD##*/}}" -TEST_HOST="${TEST_HOST:-localhost}" +TEST_HOST=${TEST_HOST:-"127.0.0.1"} echo "Integration test host: ${TEST_HOST}" -get_ipaddr() { - docker inspect --format '{{ .NetworkSettings.IPAddress }}' $1 -} - get_exposed_port() { - docker-compose port $1 $2 | cut -d: -f2 -} - -report_start() { - printf "Waiting for $1 ... " + docker compose port $1 $2 | cut -d: -f2 } -report_done() { - printf "${COLOR_GREEN}done${COLOR_RESET}\n" +wait_for() { + printf 'Waiting for %s... ' $1 + counter="0" + while true + do + if [ "$( docker compose ps | grep $1 | grep -c healthy )" -eq 1 ]; then + break + fi + counter=$((counter+1)) + if [ "${counter}" -eq 120 ]; then + echo " ERROR: container failed to start" + exit 1 + fi + sleep 1 + done + echo 'done.' } # Ensure Docker is Running @@ -42,20 +44,17 @@ then . env/bin/activate fi -mkdir -p build reports ddl/build +mkdir -p build # Stop any running instances and clean up after them, then pull images -docker-compose down --volumes --remove-orphans -docker-compose pull -q -docker-compose up -d +docker compose down --volumes --remove-orphans +docker compose pull -q +docker compose up -d + +wait_for rabbitmq -report_start "RabbitMQ" -sleep 30 -docker-compose exec -T rabbitmq rabbitmqctl await_startup -report_done +docker compose exec -T rabbitmq rabbitmqctl await_startup cat > build/test-environment<=2.3.0,<3.0", +] + +[project.optional-dependencies] +dev = [ + "codecov", + "coverage[toml]", + "mock", + "flake8", + "flake8-comprehensions", + "flake8-deprecated", + "flake8-html", + "flake8-import-order", + "flake8-pyproject", + "flake8-quotes", + "flake8-rst-docstrings", + "flake8-tuple", + "codeclimate-test-reporter", +] + +[project.urls] +Documentation = "https://rabbitpy.readthedocs.io" +Repository = "https://github.com/gmr/rabbitpy.git" +"Code Coverage" = "https://app.codecov.io/github/gmr/rabbitpy" + +[tool.flake8] +application-import-names = ["rabbitpy"] +exclude = ["build", "ci", "docs", "env"] +ignore = "RST304" +import-order-style = "google" + +[tool.coverage.xml] +output = "build/coverage.xml" + +[tool.coverage.run] +branch = true +source = ["rabbitpy"] +command_line = "-m unittest discover tests --buffer --verbose" diff --git a/rabbitpy/connection.py b/rabbitpy/connection.py index c4f399d..f8bbcc4 100644 --- a/rabbitpy/connection.py +++ b/rabbitpy/connection.py @@ -310,13 +310,23 @@ def _connect(self): self._channel0.start() # Wait for Channel0 to raise an exception or negotiate the connection - while not self._channel0.open: + timeout_start = time.time() + while time.time() < timeout_start + self.DEFAULT_TIMEOUT: + + if self._channel0.open: + break + if not self._exceptions.empty(): exception = self._exceptions.get() self._io.stop() raise exception + time.sleep(0.01) + # Raise exception if channel not opened before timeout + if not self._channel0.open: + raise exceptions.ConnectionException + # Set the maximum frame size for channel use self._max_frame_size = self._channel0.maximum_frame_size diff --git a/rabbitpy/io.py b/rabbitpy/io.py index 26d3e2c..2470ca4 100644 --- a/rabbitpy/io.py +++ b/rabbitpy/io.py @@ -579,7 +579,8 @@ def _create_socket(self, address_family, socktype, protocol): if self._args[key]: kwargs[argv] = self._args[key] LOGGER.debug('Wrapping socket for SSL: %r', kwargs) - return ssl.wrap_socket(**kwargs) + context = ssl.SSLContext() + return context.wrap_socket(**kwargs) return sock def _disconnect_socket(self): diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 3fccece..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pamqp>=2.3.0,<3.0 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index bf81030..0000000 --- a/setup.cfg +++ /dev/null @@ -1,15 +0,0 @@ -[wheel] -universal=1 - -[flake8] -application-import-names=rabbitpy -exclude=build,ci,docs,env -ignore=RST304 -import-order-style=google - -[nosetests] -with-coverage=1 -cover-package=rabbitpy -cover-branches=1 -cover-erase=1 -verbosity=2 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 43c2f6d..0000000 --- a/setup.py +++ /dev/null @@ -1,37 +0,0 @@ -import setuptools - -desc = ('A pure python, thread-safe, minimalistic and pythonic RabbitMQ ' - 'client library') - -classifiers = ['Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Topic :: Communications', - 'Topic :: Internet', - 'Topic :: Software Development :: Libraries'] - -setuptools.setup(name='rabbitpy', - version='2.0.1', - description=desc, - long_description=open('README.rst').read(), - author='Gavin M. Roy', - author_email='gavinmroy@gmail.com', - url='https://rabbitpy.readthedocs.io', - packages=['rabbitpy'], - package_data={'': ['LICENSE', 'README.md']}, - include_package_data=True, - install_requires=['pamqp>=2,<3'], - license='BSD', - classifiers=classifiers, - zip_safe=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index d2f01db..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -codecov -coverage -mock -nose -flake8 -flake8-comprehensions -flake8-deprecated -flake8-html -flake8-import-order -flake8-quotes -flake8-rst-docstrings -flake8-tuple -codeclimate-test-reporter --r requirements.txt \ No newline at end of file diff --git a/tests/base_tests.py b/tests/base_tests.py index 7b21ec5..89b8d2f 100644 --- a/tests/base_tests.py +++ b/tests/base_tests.py @@ -4,7 +4,7 @@ """ from rabbitpy import base, utils -from . import helpers +from tests import helpers class AMQPClassTests(helpers.TestCase): diff --git a/tests/channel_test.py b/tests/channel_test.py index 5fd4d60..e8e7004 100644 --- a/tests/channel_test.py +++ b/tests/channel_test.py @@ -4,7 +4,7 @@ """ from rabbitpy import exceptions -from . import helpers +from tests import helpers class ServerCapabilitiesTest(helpers.TestCase): diff --git a/tests/amqp_tests.py b/tests/test_amqp.py similarity index 95% rename from tests/amqp_tests.py rename to tests/test_amqp.py index 7daf132..6fe716b 100644 --- a/tests/amqp_tests.py +++ b/tests/test_amqp.py @@ -6,7 +6,7 @@ from rabbitpy import amqp, base, channel -from . import helpers +from tests import helpers class BasicAckTests(helpers.TestCase): diff --git a/tests/exchange_tests.py b/tests/test_exchange.py similarity index 99% rename from tests/exchange_tests.py rename to tests/test_exchange.py index c6f951a..f1007c1 100644 --- a/tests/exchange_tests.py +++ b/tests/test_exchange.py @@ -7,7 +7,7 @@ from rabbitpy import exchange -from . import helpers +from tests import helpers class TxTests(helpers.TestCase): diff --git a/tests/message_tests.py b/tests/test_message.py similarity index 99% rename from tests/message_tests.py rename to tests/test_message.py index d4c1c6e..96b7ff7 100644 --- a/tests/message_tests.py +++ b/tests/test_message.py @@ -19,7 +19,7 @@ from rabbitpy import exchange from rabbitpy import message -from . import helpers +from tests import helpers logging.basicConfig(level=logging.DEBUG) diff --git a/tests/queue_tests.py b/tests/test_queue.py similarity index 99% rename from tests/queue_tests.py rename to tests/test_queue.py index d700985..3152141 100644 --- a/tests/queue_tests.py +++ b/tests/test_queue.py @@ -10,7 +10,7 @@ from rabbitpy import exceptions from rabbitpy import utils -from . import helpers +from tests import helpers class QueueInitializationTests(helpers.TestCase): diff --git a/tests/tx_tests.py b/tests/test_tx.py similarity index 99% rename from tests/tx_tests.py rename to tests/test_tx.py index c092e1b..cd7b685 100644 --- a/tests/tx_tests.py +++ b/tests/test_tx.py @@ -7,7 +7,7 @@ from rabbitpy import exceptions, tx -from . import helpers +from tests import helpers class TxTests(helpers.TestCase):