├── tests ├── __init__.py └── test_functional.py ├── requirements ├── test.txt ├── default.txt ├── pkgutils.txt ├── docs.txt ├── test-ci.txt └── README.rst ├── funtests ├── setup.cfg ├── __init__.py ├── tests │ ├── __init__.py │ └── test_channel_errors.py ├── disabled_basic_get_leak.py ├── setup.py └── config.py ├── setup.cfg ├── manylinux1 ├── .gitmodules ├── contrib └── release │ ├── removepyc.sh │ ├── flakeplus.py │ └── bump_version.py ├── wheelhouse ├── librabbitmq-2.0.0-cp27-cp27m-manylinux1_x86_64.whl ├── librabbitmq-2.0.0-cp34-cp34m-manylinux1_x86_64.whl ├── librabbitmq-2.0.0-cp35-cp35m-manylinux1_x86_64.whl ├── librabbitmq-2.0.0-cp36-cp36m-manylinux1_x86_64.whl └── librabbitmq-2.0.0-cp27-cp27mu-manylinux1_x86_64.whl ├── TODO ├── Modules └── _librabbitmq │ ├── distmeta.h │ ├── _amqstate.h │ ├── connection.h │ └── connection.c ├── AUTHORS ├── MANIFEST.in ├── .github ├── dependabot.yml └── workflows │ └── codeql.yml ├── .pre-commit-config.yaml ├── .gitignore ├── Makefile ├── benchmark.py ├── README.rst ├── librabbitmq └── __init__.py ├── setup.py ├── Changelog ├── LICENSE-GPL-2.0 └── LICENSE-MPL-RabbitMQ /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements/test.txt: -------------------------------------------------------------------------------- 1 | mock 2 | six 3 | -------------------------------------------------------------------------------- /requirements/default.txt: -------------------------------------------------------------------------------- 1 | six>=1.0.0 2 | amqp>=1.4.6 3 | -------------------------------------------------------------------------------- /requirements/pkgutils.txt: -------------------------------------------------------------------------------- 1 | flake8 2 | flakeplus 3 | tox 4 | -------------------------------------------------------------------------------- /requirements/docs.txt: -------------------------------------------------------------------------------- 1 | Sphinx 2 | sphinxcontrib-issuetracker>=0.9 3 | -------------------------------------------------------------------------------- /requirements/test-ci.txt: -------------------------------------------------------------------------------- 1 | mock 2 | six>=1.0.0 3 | amqp>=1.4.6 4 | coverage>=3.0 5 | -------------------------------------------------------------------------------- /funtests/setup.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | verbosity = 1 3 | detailed-errors = 1 4 | where = tests 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | where = librabbitmq/tests 3 | 4 | [bdist_rpm] 5 | requires = amqp >= 1.4.6 6 | -------------------------------------------------------------------------------- /manylinux1: -------------------------------------------------------------------------------- 1 | docker run --rm -v `pwd`:/workspace:z quay.io/pypa/manylinux1_x86_64 /workspace/build-manylinux1-wheels.sh 2 | -------------------------------------------------------------------------------- /funtests/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | sys.path.insert(0, os.pardir) 5 | sys.path.insert(0, os.getcwd()) 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rabbitmq-c"] 2 | path = rabbitmq-c 3 | url = https://github.com/alanxz/rabbitmq-c.git 4 | branch = v0.8.0 5 | -------------------------------------------------------------------------------- /contrib/release/removepyc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | (cd "${1:-.}"; 3 | find . -name "*.pyc" | xargs rm -- 2>/dev/null) || echo "ok" 4 | -------------------------------------------------------------------------------- /wheelhouse/librabbitmq-2.0.0-cp27-cp27m-manylinux1_x86_64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celery/librabbitmq/master/wheelhouse/librabbitmq-2.0.0-cp27-cp27m-manylinux1_x86_64.whl -------------------------------------------------------------------------------- /wheelhouse/librabbitmq-2.0.0-cp34-cp34m-manylinux1_x86_64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celery/librabbitmq/master/wheelhouse/librabbitmq-2.0.0-cp34-cp34m-manylinux1_x86_64.whl -------------------------------------------------------------------------------- /wheelhouse/librabbitmq-2.0.0-cp35-cp35m-manylinux1_x86_64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celery/librabbitmq/master/wheelhouse/librabbitmq-2.0.0-cp35-cp35m-manylinux1_x86_64.whl -------------------------------------------------------------------------------- /wheelhouse/librabbitmq-2.0.0-cp36-cp36m-manylinux1_x86_64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celery/librabbitmq/master/wheelhouse/librabbitmq-2.0.0-cp36-cp36m-manylinux1_x86_64.whl -------------------------------------------------------------------------------- /wheelhouse/librabbitmq-2.0.0-cp27-cp27mu-manylinux1_x86_64.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celery/librabbitmq/master/wheelhouse/librabbitmq-2.0.0-cp27-cp27mu-manylinux1_x86_64.whl -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - exchange_unbind 2 | - heartbeats 3 | - publisher confirms 4 | - capabilities 5 | 6 | Also please see our Issue Tracker at GitHub: 7 | http://github.com/celery/librabbitmq/issues 8 | -------------------------------------------------------------------------------- /funtests/tests/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | sys.path.insert(0, os.path.join(os.getcwd(), os.pardir)) 5 | print(sys.path[0]) 6 | sys.path.insert(0, os.getcwd()) 7 | print(sys.path[0]) 8 | -------------------------------------------------------------------------------- /Modules/_librabbitmq/distmeta.h: -------------------------------------------------------------------------------- 1 | #define PYRABBITMQ_VERSION "2.0.0" 2 | #define PYRABBITMQ_AUTHOR "Ask Solem" 3 | #define PYRABBITMQ_CONTACT "ask@celeryproject.org" 4 | #define PYRABBITMQ_HOMEPAGE "http://github.com/celery/librabbitmq" 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Ask Solem 2 | Andrew Snowden 3 | C Anthony Risinger 4 | Eric Siegel 5 | Marcus Cobden 6 | Roger Hu 7 | Derek Anderson 8 | Daniel 10 | rmihael 11 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include Changelog 3 | include README.rst 4 | include MANIFEST.in 5 | include LICENSE-GPL-2.0 6 | include LICENSE-MPL-RabbitMQ 7 | include TODO 8 | include setup.cfg 9 | recursive-include librabbitmq * 10 | recursive-include Modules * 11 | recursive-include tests * 12 | prune rabbitmq-c/ 13 | exclude *.pyc 14 | exclude *.o 15 | exclude *.la 16 | exclude *.so 17 | exclude *.dylib 18 | exclude Makefile 19 | -------------------------------------------------------------------------------- /funtests/tests/test_channel_errors.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from config import BrokerCase 4 | 5 | from librabbitmq import ChannelError 6 | 7 | 8 | class test_channel_failures(BrokerCase): 9 | 10 | def test_channel_replaced_on_exception(self): 11 | with self.Connection() as conn: 12 | chan = conn.channel() 13 | self.assertTrue(chan) 14 | 15 | with self.assertRaises(ChannelError): 16 | chan.queue_declare(self.new_queue(False), passive=True) 17 | self.assertTrue(chan.queue_declare(self.new_queue(False))) 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Keep GitHub Actions up to date with GitHub's Dependabot... 2 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 3 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem 4 | version: 2 5 | updates: 6 | - package-ecosystem: github-actions 7 | directory: / 8 | groups: 9 | github-actions: 10 | patterns: 11 | - "*" # Group all Actions updates into a single larger pull request 12 | schedule: 13 | interval: weekly 14 | -------------------------------------------------------------------------------- /funtests/disabled_basic_get_leak.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from librabbitmq import Connection 4 | 5 | x = Connection() 6 | c = x.channel() 7 | c.exchange_declare('getmem') 8 | c.queue_declare('getmem') 9 | c.queue_bind('getmem', 'getmem', 'getmem') 10 | 11 | from time import sleep 12 | 13 | for i in range(10000): 14 | c.basic_publish('foo', exchange='getmem', routing_key='getmem') 15 | if not i % 1000: 16 | print('sent %s' % i) 17 | 18 | for i in range(10000): 19 | assert c.basic_get('getmem', no_ack=True) 20 | if not i % 1000: 21 | print(i) 22 | os.system('sh -c "ps auxww | grep %d | grep -v grep "' % os.getpid()) 23 | -------------------------------------------------------------------------------- /requirements/README.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | pip requirements files 3 | ======================== 4 | 5 | 6 | Index 7 | ===== 8 | 9 | * :file:`requirements/default.txt` 10 | 11 | Default requirements for Python 2.7+. 12 | 13 | * :file:`requirements/test.txt` 14 | 15 | Requirements needed to run the full unittest suite. 16 | 17 | * :file:`requirements/test-ci.txt` 18 | 19 | Extra test requirements required by the CI suite (Tox). 20 | 21 | * :file:`requirements/doc.txt` 22 | 23 | Extra requirements required to build the Sphinx documentation. 24 | 25 | * :file:`requirements/pkgutils.txt` 26 | 27 | Extra requirements required to perform package distribution maintenance. 28 | 29 | 30 | Examples 31 | ======== 32 | 33 | Running the tests 34 | ----------------- 35 | 36 | :: 37 | 38 | $ pip install -U -r requirements/default.txt 39 | $ pip install -U -r requirements/test.txt 40 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Learn more about this config here: https://pre-commit.com/ 2 | 3 | # To enable these pre-commit hooks run: 4 | # `brew install pre-commit` or `python3 -m pip install pre-commit` 5 | # Then in the project root directory run `pre-commit install` 6 | 7 | repos: 8 | - repo: https://github.com/pre-commit/pre-commit-hooks 9 | rev: v6.0.0 10 | hooks: 11 | # - id: check-builtin-literals 12 | # - id: check-executables-have-shebangs 13 | # - id: check-shebang-scripts-are-executable 14 | # - id: check-toml 15 | # - id: check-yaml 16 | # - id: detect-private-key 17 | - id: end-of-file-fixer 18 | # - id: mixed-line-ending 19 | # - id: trailing-whitespace 20 | 21 | - repo: https://github.com/codespell-project/codespell 22 | rev: v2.4.1 23 | hooks: 24 | - id: codespell 25 | args: 26 | - --ignore-words-list=declatory 27 | additional_dependencies: 28 | - tomli 29 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | analyze: 11 | name: Analyze 12 | runs-on: ubuntu-latest 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | language: [ cpp, python ] 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v6 26 | 27 | - name: Initialize CodeQL 28 | uses: github/codeql-action/init@v4 29 | with: 30 | languages: ${{ matrix.language }} 31 | queries: +security-and-quality 32 | 33 | - name: Autobuild 34 | uses: github/codeql-action/autobuild@v4 35 | 36 | - name: Perform CodeQL Analysis 37 | uses: github/codeql-action/analyze@v4 38 | with: 39 | category: "/language:${{ matrix.language }}" 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *~ 4 | .*.sw[po] 5 | dist/ 6 | *.egg-info 7 | *.egg 8 | *.egg/ 9 | doc/__build/* 10 | build/ 11 | .build/ 12 | pip-log.txt 13 | .directory 14 | erl_crash.dump 15 | *.db 16 | *.o 17 | *.lo 18 | *.la 19 | *.so 20 | *.dylib 21 | Documentation/ 22 | MANIFEST 23 | 24 | ^scratch$ 25 | 26 | autom4te\.cache$ 27 | 28 | config\.h 29 | config\.log 30 | stamp-h1 31 | 32 | rabbitmq-c/examples/.libs 33 | rabbitmq-c/librabbitmq/.libs 34 | tests/test_tables 35 | tests/test_parse_url 36 | rabbitmq-c/examples/amqp_sendstring 37 | rabbitmq-c/examples/amqp_exchange_declare 38 | rabbitmq-c/examples/amqp_listen 39 | rabbitmq-c/examples/amqp_producer 40 | rabbitmq-c/examples/amqp_consumer 41 | rabbitmq-c/examples/amqp_unbind 42 | rabbitmq-c/examples/amqp_bind 43 | rabbitmq-c/examples/amqp_listenq 44 | 45 | amqp_framing\.c 46 | amqp_framing\.h 47 | 48 | tools/amqp-publish 49 | tools/amqp-get 50 | tools/amqp-consume 51 | tools/amqp-declare-queue 52 | tools/amqp-delete-queue 53 | tools/.*\.[17]$ 54 | tools/doc/man-date.ent 55 | 56 | clib/ 57 | -------------------------------------------------------------------------------- /funtests/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | try: 5 | from setuptools import setup 6 | from setuptools.command.install import install 7 | except ImportError: 8 | from ez_setup import use_setuptools 9 | use_setuptools() 10 | from setuptools import setup # noqa 11 | from setuptools.command.install import install # noqa 12 | 13 | 14 | class no_install(install): 15 | 16 | def run(self, *args, **kwargs): 17 | import sys 18 | sys.stderr.write(""" 19 | ----------------------------------------------------------- 20 | The librabbitmq functional test suite cannot be installed. 21 | ----------------------------------------------------------- 22 | 23 | 24 | But you can execute the tests by running the command: 25 | 26 | $ python setup.py test 27 | 28 | 29 | """) 30 | 31 | 32 | setup( 33 | name='librabbitmq-funtests', 34 | version="DEV", 35 | description="Functional test suite for librabbitmq", 36 | author="Ask Solem", 37 | author_email="ask@celeryproject.org", 38 | url="http://github.com/celery/librabbitmq", 39 | platforms=["any"], 40 | packages=[], 41 | data_files=[], 42 | zip_safe=False, 43 | cmdclass={"install": no_install}, 44 | test_suite="tests", 45 | build_requires=[ 46 | "nose", 47 | "nose-cover3", 48 | "coverage>=3.0", 49 | ], 50 | classifiers=[ 51 | "Operating System :: OS Independent", 52 | "Programming Language :: Python", 53 | "License :: OSI Approved :: BSD License", 54 | "Intended Audience :: Developers", 55 | ], 56 | long_description="Do not install this package", 57 | ) 58 | -------------------------------------------------------------------------------- /funtests/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | BROKER_HOST = os.environ.get('BROKER_HOST', 'localhost') 4 | BROKER_PORT = int(os.environ.get('BROKER_PORT', 5672)) 5 | BROKER_VHOST = os.environ.get('BROKER_VHOST', '/') 6 | BROKER_USER = os.environ.get('BROKER_USER', 'guest') 7 | BROKER_PASSWORD = os.environ.get('BROKER_PASSWORD', 'guest') 8 | 9 | from functools import partial 10 | from unittest import TestCase 11 | from uuid import uuid4 12 | 13 | 14 | class BrokerCase(TestCase): 15 | 16 | def setUp(self): 17 | import librabbitmq 18 | self.cleanup_queues = set() 19 | self.Connection = partial(librabbitmq.Connection, 20 | host=BROKER_HOST, 21 | port=BROKER_PORT, 22 | userid=BROKER_USER, 23 | password=BROKER_PASSWORD, 24 | virtual_host=BROKER_VHOST, 25 | ) 26 | self.mod = librabbitmq 27 | self.ConnectionError = self.mod.ConnectionError 28 | self.ChannelError = self.mod.ChannelError 29 | 30 | def tearDown(self): 31 | try: 32 | for name in self.cleanup_queues: 33 | with self.Connection() as conn: 34 | with conn.channel() as chan: 35 | try: 36 | chan.queue_delete(name) 37 | except (self.ConnectionError, self.ChannelError): 38 | pass 39 | finally: 40 | self.cleanup_queues.clear() 41 | 42 | def uses_queue(self, name, register=True): 43 | register and self.cleanup_queues.add(name) 44 | return name 45 | 46 | def new_queue(self, register=True): 47 | return self.uses_queue('lrmqFUNTEST.%s' % (uuid4(), ), register) 48 | -------------------------------------------------------------------------------- /Modules/_librabbitmq/_amqstate.h: -------------------------------------------------------------------------------- 1 | #ifndef __PYRMQ_AMQSTATE_H__ 2 | #define __PYRMQ_AMQSTATE_H__ 3 | 4 | #include 5 | 6 | /* 7 bytes up front, then payload, then 1 byte footer */ 7 | #define HEADER_SIZE 7 8 | #define FOOTER_SIZE 1 9 | #define POOL_TABLE_SIZE 16 10 | 11 | typedef enum amqp_connection_state_enum_ { 12 | CONNECTION_STATE_IDLE = 0, 13 | CONNECTION_STATE_INITIAL, 14 | CONNECTION_STATE_HEADER, 15 | CONNECTION_STATE_BODY 16 | } amqp_connection_state_enum; 17 | 18 | typedef struct amqp_link_t_ { 19 | struct amqp_link_t_ *next; 20 | void *data; 21 | } amqp_link_t; 22 | 23 | typedef struct amqp_pool_table_entry_t_ { 24 | struct amqp_pool_table_entry_t_ *next; 25 | amqp_pool_t pool; 26 | amqp_channel_t channel; 27 | } amqp_pool_table_entry_t; 28 | 29 | struct amqp_connection_state_t_ { 30 | amqp_pool_table_entry_t *pool_table[POOL_TABLE_SIZE]; 31 | 32 | amqp_connection_state_enum state; 33 | 34 | int channel_max; 35 | int frame_max; 36 | int heartbeat; 37 | 38 | /* buffer for holding frame headers. Allows us to delay allocating 39 | * the raw frame buffer until the type, channel, and size are all known 40 | */ 41 | char header_buffer[HEADER_SIZE + 1]; 42 | amqp_bytes_t inbound_buffer; 43 | 44 | size_t inbound_offset; 45 | size_t target_size; 46 | 47 | amqp_bytes_t outbound_buffer; 48 | 49 | amqp_socket_t *socket; 50 | 51 | amqp_bytes_t sock_inbound_buffer; 52 | size_t sock_inbound_offset; 53 | size_t sock_inbound_limit; 54 | 55 | amqp_link_t *first_queued_frame; 56 | amqp_link_t *last_queued_frame; 57 | 58 | amqp_rpc_reply_t most_recent_api_result; 59 | 60 | uint64_t next_recv_heartbeat; 61 | uint64_t next_send_heartbeat; 62 | 63 | amqp_table_t server_properties; 64 | amqp_pool_t properties_pool; 65 | }; 66 | 67 | 68 | amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t, amqp_channel_t); 69 | 70 | #endif /* __PYRMQ_AMQSTATE_H__ */ 71 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Building 2 | RABBIT_DIR=rabbitmq-c 3 | RABBIT_DIST=librabbitmq 4 | 5 | # Distribution tools 6 | PYTHON=python 7 | 8 | all: build manylinux2014 9 | 10 | add-submodules: 11 | -git submodule add -b v0.8.0 https://github.com/alanxz/rabbitmq-c.git 12 | 13 | submodules: 14 | git submodule init 15 | git submodule update 16 | 17 | rabbitmq-c: submodules 18 | (cd $(RABBIT_DIR); test -f configure || autoreconf -i) 19 | (cd $(RABBIT_DIR); test -f Makefile || automake --add-missing) 20 | 21 | 22 | rabbitmq-clean: 23 | -(rm -rf $(RABBIT_DIR)/build || true) 24 | -(cd $(RABBIT_DIR) && make clean || echo "warning... rabbitmq-clean failed") 25 | 26 | rabbitmq-distclean: 27 | -(cd $(RABBIT_DIR) && make distclean || echo "warning... rabbitmq-distclean failed") 28 | 29 | clean-build: 30 | -rm -rf build 31 | 32 | build: clean-build dist 33 | $(PYTHON) setup.py build 34 | 35 | install: build 36 | $(PYTHON) setup.py install 37 | 38 | develop: build 39 | $(PYTHON) setup.py develop 40 | 41 | pyclean: 42 | -$(PYTHON) setup.py clean 43 | -rm -rf build 44 | -rm -f _librabbitmq.so 45 | 46 | clean: pyclean rabbitmq-clean 47 | 48 | distclean: pyclean rabbitmq-distclean removepyc 49 | -rm -rf dist 50 | -rm -rf clib 51 | -rm -f erl_crash.dump 52 | 53 | $(RABBIT_TARGET): 54 | (test -f config.h || cd $(RABBIT_DIR); ./configure --disable-tools --disable-docs) 55 | (cd $(RABBIT_DIR); make) 56 | 57 | 58 | dist: rabbitmq-c $(RABBIT_TARGET) 59 | 60 | manylinux2014: manylinux2014_x86_64 manylinux2014_aarch64 61 | 62 | manylinux2014_x86_64: dist 63 | docker run --rm -v `pwd`:/workspace:z quay.io/pypa/manylinux2014_x86_64 /workspace/build-manylinux1-wheels.sh 64 | 65 | qemu-user-static: 66 | docker run --rm --privileged hypriot/qemu-register 67 | 68 | manylinux2014_aarch64: qemu-user-static 69 | docker run --rm -v `pwd`:/workspace:z quay.io/pypa/manylinux2014_aarch64 /workspace/build-manylinux1-wheels.sh 70 | 71 | rebuild: 72 | $(PYTHON) setup.py build 73 | $(PYTHON) setup.py install 74 | 75 | 76 | # Distro tools 77 | 78 | flakecheck: 79 | flake8 librabbitmq setup.py 80 | 81 | flakediag: 82 | -$(MAKE) flakecheck 83 | 84 | flakepluscheck: 85 | flakeplus librabbitmq 86 | 87 | flakeplusdiag: 88 | -$(MAKE) flakepluscheck 89 | 90 | flakes: flakediag flakeplusdiag 91 | 92 | test: build 93 | $(PYTHON) setup.py test 94 | 95 | cov: build 96 | coverage run --source=librabbitmq setup.py test 97 | coverage report 98 | 99 | removepyc: 100 | -find . -type f -a \( -name "*.pyc" -o -name "*$$py.class" \) | xargs rm 101 | -find . -type d -name "__pycache__" | xargs rm -r 102 | 103 | gitclean: 104 | git clean -xdn 105 | 106 | gitcleanforce: 107 | git clean -xdf 108 | 109 | distcheck: flakecheck test gitclean 110 | -------------------------------------------------------------------------------- /benchmark.py: -------------------------------------------------------------------------------- 1 | import timeit 2 | 3 | INIT_COMMON = """ 4 | connection = amqp.Connection(hostname="localhost", userid="guest", 5 | password="guest", virtual_host="/", lazy=True) 6 | connection.connect() 7 | channel = connection.channel() 8 | channel.exchange_declare(Q, "direct") 9 | channel.queue_declare(Q) 10 | channel.queue_bind(Q, Q, Q) 11 | """ 12 | 13 | INIT_AMQP = """ 14 | import amqp 15 | Q = "amqp.benchmark" 16 | %s 17 | """ % INIT_COMMON 18 | 19 | INIT_LIBRABBIT = """ 20 | import librabbitmq as amqp 21 | 22 | Q = "librabbit.benchmark" 23 | %s 24 | """ % INIT_COMMON 25 | 26 | PUBLISH = """ 27 | message = amqp.Message("x" * %d) 28 | channel.basic_publish(message, exchange=Q, routing_key=Q) 29 | """ 30 | 31 | PUBLISH_LIBRABBIT = """ 32 | connection._basic_publish(1, "x" * %d, Q, Q, {}) 33 | """ 34 | 35 | CONSUME = """ 36 | method = connection.drain_events 37 | def callback(m): 38 | channel.basic_ack(m.delivery_info["delivery_tag"]) 39 | channel.basic_consume(Q, callback=callback) 40 | for i in range(%(its)d): 41 | method() 42 | """ 43 | 44 | 45 | def bench_basic_publish(iterations=10000, bytes=256): 46 | t_publish_amqp = timeit.Timer(stmt=PUBLISH % bytes, 47 | setup=INIT_AMQP) 48 | t_publish_librabbit = timeit.Timer(stmt=PUBLISH_LIBRABBIT % bytes, 49 | setup=INIT_LIBRABBIT) 50 | print("basic.publish: (%s x %s bytes messages)" % (iterations, bytes)) 51 | print(" amqp: %.2f sec/pass" % ( 52 | iterations * t_publish_amqp.timeit(number=iterations)/iterations) 53 | ) 54 | print(" librabbit: %.2f sec/pass" % ( 55 | iterations * t_publish_librabbit.timeit(number=iterations)/iterations) 56 | ) 57 | 58 | def bench_basic_consume(iterations=10000, bytes=None): 59 | context = {"its": (iterations/2)/10} 60 | t_consume_amqp = timeit.Timer(stmt=CONSUME % context, 61 | setup=INIT_AMQP) 62 | t_consume_librabbit = timeit.Timer(stmt=CONSUME % context, 63 | setup=INIT_LIBRABBIT) 64 | print("basic.consume (%s msg/pass) " % context["its"]) 65 | print(" amqp: %.2f sec/pass" % ( 66 | t_consume_amqp.timeit(number=10)) 67 | ) 68 | print(" librabbit: %.2f sec/pass" % ( 69 | t_consume_librabbit.timeit(number=10)) 70 | ) 71 | 72 | benchmarks = [bench_basic_publish, bench_basic_consume] 73 | 74 | if __name__ == "__main__": 75 | import argparse 76 | 77 | parser = argparse.ArgumentParser(description='Runs benchmark against local RabbitMQ instance.') 78 | parser.add_argument('--iters', metavar='N', type=int, default=100000, 79 | help='Number of iterations') 80 | parser.add_argument('--bytes', metavar='B', type=int, 81 | default=256, help='Message size') 82 | 83 | args = parser.parse_args() 84 | for benchmark in benchmarks: 85 | benchmark(args.iters, bytes=args.bytes) 86 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================================================================ 2 | librabbitmq - Python AMQP Client using the rabbitmq-c library. 3 | ================================================================ 4 | 5 | :Version: 2.0.0 6 | :Download: http://pypi.python.org/pypi/librabbitmq/ 7 | :Code: http://github.com/celery/librabbitmq/ 8 | :DeepWiki: |deepwiki| 9 | :Keywords: rabbitmq, amqp, messaging, librabbitmq, rabbitmq-c, python, 10 | kombu, celery 11 | 12 | .. |deepwiki| image:: https://devin.ai/assets/deepwiki-badge.png 13 | :alt: Ask http://DeepWiki.com 14 | :target: https://deepwiki.com/celery/librabbitmq 15 | :width: 125px 16 | 17 | .. contents:: 18 | :local: 19 | 20 | Python bindings to the RabbitMQ C-library `rabbitmq-c`_. 21 | Supported by Kombu and Celery. 22 | 23 | .. _`rabbitmq-c`: https://github.com/alanxz/rabbitmq-c 24 | 25 | Installation 26 | ============ 27 | 28 | Install via pip:: 29 | 30 | $ pip install librabbitmq 31 | 32 | or, install via easy_install:: 33 | 34 | $ easy_install librabbitmq 35 | 36 | Downloading and installing from source 37 | -------------------------------------- 38 | 39 | Download the latest version from 40 | http://pypi.python.org/pypi/librabbitmq/ 41 | 42 | Then install it by doing the following,:: 43 | 44 | $ tar xvfz librabbitmq-0.0.0.tar.gz 45 | $ cd librabbitmq-0.0.0 46 | $ python setup.py build 47 | # python setup.py install # as root 48 | 49 | Using the development version 50 | ----------------------------- 51 | 52 | You can clone the repository by doing the following:: 53 | 54 | $ git clone git://github.com/celery/librabbitmq.git 55 | 56 | Then install it by doing the following:: 57 | 58 | $ cd librabbitmq 59 | $ make install # or make develop 60 | 61 | Examples 62 | ======== 63 | 64 | Using with Kombu:: 65 | 66 | >>> from kombu import Connection 67 | >>> x = Connection("librabbitmq://") 68 | 69 | 70 | Stand-alone:: 71 | 72 | >>> from librabbitmq import Connection 73 | 74 | >>> conn = Connection(host="localhost", userid="guest", 75 | ... password="guest", virtual_host="/") 76 | 77 | >>> channel = conn.channel() 78 | >>> channel.exchange_declare(exchange, type, ...) 79 | >>> channel.queue_declare(queue, ...) 80 | >>> channel.queue_bind(queue, exchange, routing_key) 81 | 82 | Producing 83 | --------- 84 | 85 | :: 86 | 87 | >>> channel.basic_publish(body, exchange, routing_key, ...) 88 | 89 | Consuming 90 | --------- 91 | 92 | :: 93 | 94 | >>> def dump_message(message): 95 | ... print("Body:'%s', Properties:'%s', DeliveryInfo:'%s'" % ( 96 | ... message.body, message.properties, message.delivery_info)) 97 | ... message.ack() 98 | 99 | >>> channel.basic_consume(queue, ..., callback=dump_message) 100 | 101 | >>> while True: 102 | ... connection.drain_events() 103 | 104 | Poll 105 | ---- 106 | 107 | :: 108 | 109 | >>> message = channel.basic_get(queue, ...) 110 | >>> if message: 111 | ... dump_message(message) 112 | ... print("Body:'%s' Properties:'%s' DeliveryInfo:'%s'" % ( 113 | ... message.body, message.properties, message.delivery_info)) 114 | 115 | 116 | Other 117 | ----- 118 | 119 | :: 120 | 121 | >>> channel.queue_unbind(queue, ...) 122 | >>> channel.close() 123 | >>> connection.close() 124 | 125 | License 126 | ======= 127 | 128 | This software is licensed under the ``Mozilla Public License``. 129 | See the ``LICENSE-MPL-RabbitMQ`` file in the top distribution directory 130 | for the full license text. 131 | 132 | .. # vim: syntax=rst expandtab tabstop=4 shiftwidth=4 shiftround 133 | -------------------------------------------------------------------------------- /contrib/release/flakeplus.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import absolute_import 3 | from __future__ import with_statement 4 | 5 | import os 6 | import re 7 | import sys 8 | 9 | from collections import defaultdict 10 | from unipath import Path 11 | 12 | RE_COMMENT = r'^\s*\#' 13 | RE_NOQA = r'.+?\#\s+noqa+' 14 | RE_MULTILINE_COMMENT_O = r'^\s*(?:\'\'\'|""").+?(?:\'\'\'|""")' 15 | RE_MULTILINE_COMMENT_S = r'^\s*(?:\'\'\'|""")' 16 | RE_MULTILINE_COMMENT_E = r'(?:^|.+?)(?:\'\'\'|""")' 17 | RE_WITH = r'(?:^|\s+)with\s+' 18 | RE_WITH_IMPORT = r'''from\s+ __future__\s+ import\s+ with_statement''' 19 | RE_PRINT = r'''(?:^|\s+)print\((?:"|')(?:\W+?)?[A-Z0-9:]{2,}''' 20 | RE_ABS_IMPORT = r'''from\s+ __future__\s+ import\s+ absolute_import''' 21 | 22 | acc = defaultdict(lambda: {"abs": False, "print": False}) 23 | 24 | 25 | def compile(regex): 26 | return re.compile(regex, re.VERBOSE) 27 | 28 | 29 | class FlakePP(object): 30 | re_comment = compile(RE_COMMENT) 31 | re_ml_comment_o = compile(RE_MULTILINE_COMMENT_O) 32 | re_ml_comment_s = compile(RE_MULTILINE_COMMENT_S) 33 | re_ml_comment_e = compile(RE_MULTILINE_COMMENT_E) 34 | re_abs_import = compile(RE_ABS_IMPORT) 35 | re_print = compile(RE_PRINT) 36 | re_with_import = compile(RE_WITH_IMPORT) 37 | re_with = compile(RE_WITH) 38 | re_noqa = compile(RE_NOQA) 39 | map = {"abs": True, "print": False, 40 | "with": False, "with-used": False} 41 | 42 | def __init__(self, verbose=False): 43 | self.verbose = verbose 44 | self.steps = (("abs", self.re_abs_import), 45 | ("with", self.re_with_import), 46 | ("with-used", self.re_with), 47 | ("print", self.re_print)) 48 | 49 | def analyze_fh(self, fh): 50 | steps = self.steps 51 | filename = fh.name 52 | acc = dict(self.map) 53 | index = 0 54 | errors = [0] 55 | 56 | def error(fmt, **kwargs): 57 | errors[0] += 1 58 | self.announce(fmt, **dict(kwargs, filename=filename)) 59 | 60 | for index, line in enumerate(self.strip_comments(fh)): 61 | for key, pattern in steps: 62 | if pattern.match(line): 63 | acc[key] = True 64 | if index: 65 | if not acc["abs"]: 66 | error("%(filename)s: missing abs import") 67 | if acc["with-used"] and not acc["with"]: 68 | error("%(filename)s: missing with import") 69 | if acc["print"]: 70 | error("%(filename)s: left over print statement") 71 | 72 | return filename, errors[0], acc 73 | 74 | def analyze_file(self, filename): 75 | with open(filename) as fh: 76 | return self.analyze_fh(fh) 77 | 78 | def analyze_tree(self, dir): 79 | for dirpath, _, filenames in os.walk(dir): 80 | for path in (Path(dirpath, f) for f in filenames): 81 | if path.endswith(".py"): 82 | yield self.analyze_file(path) 83 | 84 | def analyze(self, *paths): 85 | for path in map(Path, paths): 86 | if path.isdir(): 87 | for res in self.analyze_tree(path): 88 | yield res 89 | else: 90 | yield self.analyze_file(path) 91 | 92 | def strip_comments(self, fh): 93 | re_comment = self.re_comment 94 | re_ml_comment_o = self.re_ml_comment_o 95 | re_ml_comment_s = self.re_ml_comment_s 96 | re_ml_comment_e = self.re_ml_comment_e 97 | re_noqa = self.re_noqa 98 | in_ml = False 99 | 100 | for line in fh.readlines(): 101 | if in_ml: 102 | if re_ml_comment_e.match(line): 103 | in_ml = False 104 | else: 105 | if re_noqa.match(line) or re_ml_comment_o.match(line): 106 | pass 107 | elif re_ml_comment_s.match(line): 108 | in_ml = True 109 | elif re_comment.match(line): 110 | pass 111 | else: 112 | yield line 113 | 114 | def announce(self, fmt, **kwargs): 115 | sys.stderr.write((fmt + "\n") % kwargs) 116 | 117 | 118 | def main(argv=sys.argv, exitcode=0): 119 | for _, errors, _ in FlakePP(verbose=True).analyze(*argv[1:]): 120 | if errors: 121 | exitcode = 1 122 | return exitcode 123 | 124 | 125 | if __name__ == "__main__": 126 | sys.exit(main()) 127 | -------------------------------------------------------------------------------- /contrib/release/bump_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | from __future__ import with_statement 5 | 6 | import errno 7 | import os 8 | import re 9 | import sys 10 | import subprocess 11 | 12 | from contextlib import contextmanager 13 | from tempfile import NamedTemporaryFile 14 | 15 | rq = lambda s: s.strip("\"'") 16 | 17 | 18 | def cmd(*args): 19 | return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0] 20 | 21 | 22 | @contextmanager 23 | def no_enoent(): 24 | try: 25 | yield 26 | except OSError as exc: 27 | if exc.errno != errno.ENOENT: 28 | raise 29 | 30 | 31 | class StringVersion(object): 32 | 33 | def decode(self, s): 34 | s = rq(s) 35 | text = "" 36 | major, minor, release = s.split(".") 37 | if not release.isdigit(): 38 | pos = release.index(re.split("\d+", release)[1][0]) 39 | release, text = release[:pos], release[pos:] 40 | return int(major), int(minor), int(release), text 41 | 42 | def encode(self, v): 43 | return ".".join(map(str, v[:3])) + v[3] 44 | to_str = StringVersion().encode 45 | from_str = StringVersion().decode 46 | 47 | 48 | class TupleVersion(object): 49 | 50 | def decode(self, s): 51 | v = list(map(rq, s.split(", "))) 52 | return (tuple(map(int, v[0:3])) + 53 | tuple(["".join(v[3:])])) 54 | 55 | def encode(self, v): 56 | v = list(v) 57 | 58 | def quote(lit): 59 | if isinstance(lit, basestring): 60 | return '"%s"' % (lit, ) 61 | return str(lit) 62 | 63 | if not v[-1]: 64 | v.pop() 65 | return ", ".join(map(quote, v)) 66 | 67 | 68 | class VersionFile(object): 69 | 70 | def __init__(self, filename): 71 | self.filename = filename 72 | self._kept = None 73 | 74 | def _as_orig(self, version): 75 | return self.wb % {"version": self.type.encode(version), 76 | "kept": self._kept} 77 | 78 | def write(self, version): 79 | pattern = self.regex 80 | with no_enoent(): 81 | with NamedTemporaryFile() as dest: 82 | with open(self.filename) as orig: 83 | for line in orig: 84 | if pattern.match(line): 85 | dest.write(self._as_orig(version)) 86 | else: 87 | dest.write(line) 88 | os.rename(dest.name, self.filename) 89 | 90 | def parse(self): 91 | pattern = self.regex 92 | gpos = 0 93 | with open(self.filename) as fh: 94 | for line in fh: 95 | m = pattern.match(line) 96 | if m: 97 | if "?P" in pattern.pattern: 98 | self._kept, gpos = m.groupdict()["keep"], 1 99 | return self.type.decode(m.groups()[gpos]) 100 | 101 | 102 | class PyVersion(VersionFile): 103 | regex = re.compile(r'^VERSION\s*=\s*\((.+?)\)') 104 | wb = "VERSION = (%(version)s)\n" 105 | type = TupleVersion() 106 | 107 | 108 | class SphinxVersion(VersionFile): 109 | regex = re.compile(r'^:[Vv]ersion:\s*(.+?)$') 110 | wb = ':Version: %(version)s\n' 111 | type = StringVersion() 112 | 113 | 114 | class CPPVersion(VersionFile): 115 | regex = re.compile(r'^\#\s*define\s*(?P\w*)VERSION\s+(.+)') 116 | wb = '#define %(kept)sVERSION "%(version)s"\n' 117 | type = StringVersion() 118 | 119 | 120 | _filetype_to_type = {"py": PyVersion, 121 | "rst": SphinxVersion, 122 | "c": CPPVersion, 123 | "h": CPPVersion} 124 | 125 | def filetype_to_type(filename): 126 | _, _, suffix = filename.rpartition(".") 127 | return _filetype_to_type[suffix](filename) 128 | 129 | 130 | def bump(*files, **kwargs): 131 | version = kwargs.get("version") 132 | files = [filetype_to_type(f) for f in files] 133 | versions = [v.parse() for v in files] 134 | current = list(reversed(sorted(versions)))[0] # find highest 135 | 136 | if version: 137 | next = from_str(version) 138 | else: 139 | major, minor, release, text = current 140 | if text: 141 | raise Exception("Can't bump alpha releases") 142 | next = (major, minor, release + 1, text) 143 | 144 | print("Bump version from %s -> %s" % (to_str(current), to_str(next))) 145 | 146 | for v in files: 147 | print(" writing %r..." % (v.filename, )) 148 | v.write(next) 149 | 150 | print(cmd("git", "commit", "-m", "Bumps version to %s" % (to_str(next), ), 151 | *[f.filename for f in files])) 152 | print(cmd("git", "tag", "v%s" % (to_str(next), ))) 153 | 154 | 155 | def main(argv=sys.argv, version=None): 156 | if not len(argv) > 1: 157 | print("Usage: distdir [docfile] -- ") 158 | sys.exit(0) 159 | if "--" in argv: 160 | c = argv.index('--') 161 | version = argv[c + 1] 162 | argv = argv[:c] 163 | bump(*argv[1:], version=version) 164 | 165 | if __name__ == "__main__": 166 | main() 167 | -------------------------------------------------------------------------------- /librabbitmq/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import sys 4 | import itertools 5 | from six.moves import xrange 6 | 7 | import _librabbitmq 8 | 9 | from amqp.protocol import queue_declare_ok_t 10 | from array import array 11 | 12 | __version__ = _librabbitmq.__version__ 13 | __author__ = _librabbitmq.__author__ 14 | __contact__ = _librabbitmq.__contact__ 15 | __homepage__ = _librabbitmq.__homepage__ 16 | __docformat__ = 'restructuredtext' 17 | 18 | ConnectionError = _librabbitmq.ConnectionError 19 | ChannelError = _librabbitmq.ChannelError 20 | 21 | 22 | __version__ = '2.0.0' 23 | __all__ = ['Connection', 'Message', 'ConnectionError', 'ChannelError'] 24 | 25 | 26 | class Message(object): 27 | 28 | def __init__(self, channel, properties, delivery_info={}, body=''): 29 | self.channel = channel 30 | self.properties = properties 31 | self.delivery_info = delivery_info 32 | self.body = body 33 | 34 | def ack(self): 35 | return self.channel.basic_ack(self.delivery_info['delivery_tag']) 36 | 37 | def reject(self): 38 | return self.channel.basic_reject(self.delivery_info['delivery_tag']) 39 | 40 | 41 | class Channel(object): 42 | Message = Message 43 | is_open = False 44 | 45 | def __init__(self, connection, channel_id): 46 | self.connection = connection 47 | self.channel_id = channel_id 48 | if sys.version_info.major == 2: 49 | self.next_consumer_tag = itertools.count(1).next 50 | else: 51 | self.next_consumer_tag = itertools.count(1).__next__ 52 | self.no_ack_consumers = set() 53 | 54 | def __enter__(self): 55 | return self 56 | 57 | def __exit__(self, *exc_info): 58 | self.close() 59 | 60 | def basic_qos(self, prefetch_size=0, prefetch_count=0, _global=False): 61 | return self.connection._basic_qos( 62 | self.channel_id, prefetch_size, prefetch_count, _global, 63 | ) 64 | 65 | def flow(self, active): 66 | return self.connection._flow(self.channel_id, active) 67 | 68 | def basic_recover(self, requeue=True): 69 | return self.connection._basic_recover(self.channel_id, requeue) 70 | 71 | def basic_get(self, queue='', no_ack=False): 72 | frame = self.connection._basic_get(self.channel_id, queue, no_ack) 73 | if frame is not None: 74 | return self.Message( 75 | self, frame['properties'], 76 | frame['delivery_info'], frame['body'], 77 | ) 78 | 79 | def basic_consume(self, queue='', consumer_tag=None, no_local=False, 80 | no_ack=False, exclusive=False, callback=None, 81 | arguments=None, nowait=False): 82 | if consumer_tag is None: 83 | consumer_tag = self.next_consumer_tag() 84 | consumer_tag = self.connection._basic_consume( 85 | self.channel_id, queue, str(consumer_tag), 86 | no_local, no_ack, exclusive, arguments or {}, 87 | ) 88 | self.connection.callbacks[self.channel_id][consumer_tag] = callback 89 | if no_ack: 90 | self.no_ack_consumers.add(consumer_tag) 91 | return consumer_tag 92 | 93 | def basic_ack(self, delivery_tag, multiple=False): 94 | return self.connection._basic_ack( 95 | self.channel_id, delivery_tag, multiple, 96 | ) 97 | 98 | def basic_reject(self, delivery_tag, requeue=True): 99 | return self.connection._basic_reject( 100 | self.channel_id, delivery_tag, requeue, 101 | ) 102 | 103 | def basic_cancel(self, consumer_tag, **kwargs): 104 | self.no_ack_consumers.discard(consumer_tag) 105 | if self.connection: 106 | try: 107 | callbacks = self.connection.callbacks[self.channel_id] 108 | except KeyError: 109 | pass 110 | else: 111 | callbacks.pop(consumer_tag, None) 112 | self.connection._basic_cancel(self.channel_id, consumer_tag) 113 | 114 | def basic_publish(self, body, exchange='', routing_key='', 115 | mandatory=False, immediate=False, **properties): 116 | if isinstance(body, tuple): 117 | body, properties = body 118 | elif isinstance(body, self.Message): 119 | body, properties = body.body, body.properties 120 | return self.connection._basic_publish( 121 | self.channel_id, body, exchange, routing_key, properties, 122 | mandatory or False, immediate or False, 123 | ) 124 | 125 | def queue_purge(self, queue, nowait=False): 126 | return self.connection._queue_purge(self.channel_id, queue, nowait) 127 | 128 | def exchange_declare(self, exchange='', type='direct', 129 | passive=False, durable=False, auto_delete=False, 130 | arguments=None, nowait=False): 131 | """Declare exchange. 132 | 133 | :keyword auto_delete: Not recommended and so it is ignored. 134 | 135 | """ 136 | return self.connection._exchange_declare( 137 | self.channel_id, exchange, type, passive, durable, 138 | auto_delete, arguments or {}, 139 | ) 140 | 141 | def exchange_delete(self, exchange='', if_unused=False, nowait=False): 142 | return self.connection._exchange_delete( 143 | self.channel_id, exchange, if_unused, 144 | ) 145 | 146 | def queue_declare(self, queue='', passive=False, durable=False, 147 | exclusive=False, auto_delete=False, arguments=None, 148 | nowait=False): 149 | return queue_declare_ok_t( 150 | *self.connection._queue_declare( 151 | self.channel_id, queue, passive, durable, 152 | exclusive, auto_delete, arguments or {}, 153 | ) 154 | ) 155 | 156 | def queue_bind(self, queue='', exchange='', routing_key='', 157 | arguments=None, nowait=False): 158 | return self.connection._queue_bind( 159 | self.channel_id, queue, exchange, routing_key, arguments or {}, 160 | ) 161 | 162 | def queue_unbind(self, queue='', exchange='', routing_key='', 163 | arguments=None, nowait=False): 164 | return self.connection._queue_unbind( 165 | self.channel_id, queue, exchange, routing_key, arguments or {}, 166 | ) 167 | 168 | def queue_delete(self, queue='', 169 | if_unused=False, if_empty=False, nowait=False): 170 | """nowait argument is not supported.""" 171 | return self.connection._queue_delete( 172 | self.channel_id, queue, if_unused, if_empty, 173 | ) 174 | 175 | def close(self): 176 | if self.connection: 177 | self.connection._remove_channel(self) 178 | 179 | 180 | class Connection(_librabbitmq.Connection): 181 | """Create a connection to the specified host, which should be 182 | a ``'host[:port]'`` string, such as ``'localhost'``, or ``'1.2.3.4:5672'`` 183 | 184 | Host defaults to ``'localhost'``, if a port is not specified 185 | then 5672 is used. 186 | 187 | """ 188 | Message = Message 189 | Channel = Channel 190 | 191 | def __init__(self, host='localhost', userid='guest', password='guest', 192 | virtual_host='/', port=5672, channel_max=0xffff, 193 | frame_max=131072, heartbeat=0, lazy=False, 194 | client_properties=None, connect_timeout=None, **kwargs): 195 | if ':' in host: 196 | host, port = host.split(':') 197 | super(Connection, self).__init__( 198 | hostname=host, port=int(port), userid=userid, password=password, 199 | virtual_host=virtual_host, channel_max=channel_max, 200 | frame_max=frame_max, heartbeat=heartbeat, 201 | client_properties=client_properties, 202 | connect_timeout=0 if connect_timeout is None else int(connect_timeout), 203 | ) 204 | self.channels = {} 205 | self._used_channel_ids = array('H') 206 | if not lazy: 207 | self.connect() 208 | 209 | def __enter__(self): 210 | return self 211 | 212 | def __exit__(self, *exc_info): 213 | self.close() 214 | 215 | def reconnect(self): 216 | self.close() 217 | self.connect() 218 | 219 | def drain_events(self, timeout=None): 220 | # we rewrite to socket.timeout here, as this is what kombu-patched 221 | # amqplib uses. 222 | if timeout == 0.0: 223 | timeout = -1 224 | elif timeout is None: 225 | timeout = 0.0 226 | else: 227 | timeout = float(timeout) 228 | self._basic_recv(timeout) 229 | 230 | def channel(self, channel_id=None): 231 | if channel_id is None: 232 | channel_id = self._get_free_channel_id() 233 | elif channel_id in self.channels: 234 | return self.channels[channel_id] 235 | 236 | self._channel_open(channel_id) 237 | self.callbacks[channel_id] = {} 238 | channel = self.Channel(self, channel_id) 239 | channel.is_open = True 240 | self.channels[channel_id] = channel 241 | return channel 242 | 243 | def _remove_channel(self, channel): 244 | channel.is_open = False 245 | try: 246 | self._channel_close(channel.channel_id) 247 | except ChannelError: 248 | pass 249 | self.channels.pop(channel.channel_id, None) 250 | self.callbacks.pop(channel.channel_id, None) 251 | try: 252 | self._used_channel_ids.remove(channel.channel_id) 253 | except ValueError: 254 | # channel id already removed 255 | pass 256 | 257 | def _get_free_channel_id(self): 258 | # Cast to a set for fast lookups, and keep stored as an array for lower memory usage. 259 | used_channel_ids = set(self._used_channel_ids) 260 | 261 | for channel_id in range(1, self.channel_max + 1): 262 | if channel_id not in used_channel_ids: 263 | self._used_channel_ids.append(channel_id) 264 | return channel_id 265 | 266 | raise ConnectionError( 267 | 'No free channel ids, current=%d, channel_max=%d' % ( 268 | len(self.channels), self.channel_max)) 269 | 270 | def close(self): 271 | try: 272 | self._close() 273 | except ConnectionError: 274 | pass 275 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import sys 4 | from setuptools import setup, find_packages 5 | 6 | # --with-librabbitmq=: path to librabbitmq package if needed 7 | 8 | BASE_PATH = os.path.dirname(__file__) 9 | 10 | LRMQDIST = lambda *x: os.path.join(BASE_PATH, 'rabbitmq-c', *x) 11 | LRMQSRC = lambda *x: LRMQDIST('librabbitmq', *x) 12 | PYCP = lambda *x: os.path.join(BASE_PATH, 'Modules', '_librabbitmq', *x) 13 | 14 | 15 | def senv(*k__v, **kwargs): 16 | sep = kwargs.get('sep', ' ') 17 | restore = {} 18 | for k, v in k__v: 19 | prev = restore[k] = os.environ.get(k) 20 | os.environ[k] = (prev + sep if prev else '') + str(v) 21 | return dict((k, v) for k, v in restore.items() if v is not None) 22 | 23 | 24 | def create_builder(): 25 | from setuptools import Extension 26 | from distutils.command.build import build as _build 27 | cmd = None 28 | pkgdirs = [] # incdirs and libdirs get these 29 | libs = [] 30 | defs = [] 31 | incdirs = [] 32 | libdirs = [] 33 | 34 | def append_env(L, e): 35 | v = os.environ.get(e) 36 | if v and os.path.exists(v): 37 | L.append(v) 38 | 39 | append_env(pkgdirs, 'LIBRABBITMQ') 40 | 41 | # Hack up sys.argv, yay 42 | unprocessed = [] 43 | for arg in sys.argv[1:]: 44 | if arg == '--gen-setup': 45 | cmd = arg[2:] 46 | elif '=' in arg: 47 | if arg.startswith('--with-librabbitmq='): 48 | pkgdirs.append(arg.split('=', 1)[1]) 49 | continue 50 | unprocessed.append(arg) 51 | sys.argv[1:] = unprocessed 52 | 53 | incdirs.append(LRMQSRC()) 54 | if find_cmake() != "": 55 | incdirs.append(LRMQDIST('build', 'librabbitmq')) 56 | 57 | 58 | PyC_files = map(PYCP, [ 59 | 'connection.c', 60 | ]) 61 | librabbit_files = map(LRMQSRC, [ 62 | 'amqp_api.c', 63 | 'amqp_connection.c', 64 | 'amqp_consumer.c', 65 | 'amqp_framing.c', 66 | 'amqp_hostcheck.c', 67 | 'amqp_mem.c', 68 | 'amqp_socket.c', 69 | 'amqp_table.c', 70 | 'amqp_tcp_socket.c', 71 | 'amqp_time.c', 72 | 'amqp_url.c', 73 | ]) 74 | 75 | incdirs.append(LRMQDIST()) # for config.h 76 | 77 | if is_linux: # Issue #42 78 | libs.append('rt') # -lrt for clock_gettime 79 | 80 | librabbitmq_ext = Extension( 81 | '_librabbitmq', 82 | sources=list(PyC_files) + list(librabbit_files), 83 | libraries=libs, include_dirs=incdirs, 84 | library_dirs=libdirs, define_macros=defs, 85 | ) 86 | 87 | # Hidden secret: 88 | # If environment variable GEN_SETUP is set, generate Setup file. 89 | if cmd == 'gen-setup': 90 | line = ' '.join(( 91 | librabbitmq_ext.name, 92 | ' '.join('-l' + lib for lib in librabbitmq_ext.libraries), 93 | ' '.join('-I' + incdir for incdir in librabbitmq_ext.include_dirs), 94 | ' '.join('-L' + libdir for libdir in librabbitmq_ext.library_dirs), 95 | ' '.join('-D' + name + ('=' + str(value), '')[value is None] 96 | for name, value in librabbitmq_ext.define_macros))) 97 | open('Setup', 'w').write(line + '\n') 98 | sys.exit(0) 99 | 100 | class build(_build): 101 | stdcflags = [ 102 | '-DHAVE_CONFIG_H', 103 | ] 104 | if os.environ.get('PEDANTIC'): 105 | # Python.h breaks -pedantic, so can only use it while developing. 106 | stdcflags.append('-pedantic -Werror') 107 | 108 | def run(self): 109 | from distutils import sysconfig 110 | 111 | here = os.path.abspath(os.getcwd()) 112 | config = sysconfig.get_config_vars() 113 | make = find_make() 114 | cmake = find_cmake() 115 | 116 | try: 117 | vars = {'ld': config['LDFLAGS'], 118 | 'c': config['CFLAGS']} 119 | for key in list(vars): 120 | vars[key] = vars[key].replace('-lSystem', '') 121 | # Python on Maverics sets this, but not supported on clang 122 | vars[key] = vars[key].replace('-mno-fused-madd', '') 123 | vars[key] = vars[key].replace( 124 | '-isysroot /Developer/SDKs/MacOSX10.6.sdk', '') 125 | restore = senv( 126 | ('CFLAGS', vars['c']), 127 | ('LDFLAGS', vars['ld']), 128 | ) 129 | 130 | try: 131 | if not os.path.isdir(os.path.join(LRMQDIST(), '.git')): 132 | print('- pull submodule rabbitmq-c...') 133 | if os.path.isfile('Makefile'): 134 | os.system(' '.join([make, 'submodules'])) 135 | else: 136 | os.system(' '.join(['git', 'clone', '-b', 'v0.8.0', 137 | 'https://github.com/alanxz/rabbitmq-c.git', 138 | 'rabbitmq-c'])) 139 | 140 | os.chdir(LRMQDIST()) 141 | 142 | if cmake == "" and not os.path.isfile('configure'): 143 | print('- autoreconf') 144 | os.system('autoreconf -i') 145 | 146 | if cmake == "" and not os.path.isfile('config.h'): 147 | print('- configure rabbitmq-c...') 148 | if os.system('/bin/sh configure --disable-tools \ 149 | --disable-docs --disable-dependency-tracking'): 150 | return 151 | 152 | if cmake: 153 | print('- cmake rabbitmq-c...') 154 | if os.system('mkdir -p build'): 155 | return 156 | 157 | os.chdir('build') 158 | if not os.path.isfile('Makefile'): 159 | if os.system(cmake + ' ..'): 160 | return 161 | 162 | if os.system(make + ' rabbitmq rabbitmq-static'): 163 | return 164 | 165 | finally: 166 | os.environ.update(restore) 167 | finally: 168 | os.chdir(here) 169 | restore = senv( 170 | ('CFLAGS', ' '.join(self.stdcflags)), 171 | ) 172 | try: 173 | _build.run(self) 174 | finally: 175 | os.environ.update(restore) 176 | return librabbitmq_ext, build 177 | 178 | 179 | def find_make(alt=('gmake', 'gnumake', 'make', 'nmake')): 180 | for path in os.environ['PATH'].split(':'): 181 | for make in (os.path.join(path, m) for m in alt): 182 | if os.path.isfile(make): 183 | return make 184 | 185 | 186 | def find_cmake(): 187 | for path in os.environ['PATH'].split(':'): 188 | make = os.path.join(path, 'cmake') 189 | if os.path.isfile(make): 190 | return make 191 | 192 | return "" 193 | 194 | 195 | with open(os.path.join(BASE_PATH, 'README.rst')) as f: 196 | long_description = f.read() 197 | 198 | distmeta = open(PYCP('distmeta.h')).read().strip().splitlines() 199 | distmeta = [item.split('\"')[1] for item in distmeta] 200 | version = distmeta[0].strip() 201 | author = distmeta[1].strip() 202 | contact = distmeta[2].strip() 203 | homepage = distmeta[3].strip() 204 | 205 | ext_modules = [] 206 | cmdclass = {} 207 | packages = [] 208 | goahead = False 209 | is_jython = sys.platform.startswith('java') 210 | is_pypy = hasattr(sys, 'pypy_version_info') 211 | is_win = platform.system() == 'Windows' 212 | is_linux = platform.system() == 'Linux' 213 | if is_jython or is_pypy or is_win: 214 | pass 215 | elif find_make(): 216 | try: 217 | librabbitmq_ext, build = create_builder() 218 | except Exception as exc: 219 | print('Could not create builder: %r' % (exc, )) 220 | raise 221 | else: 222 | goahead = True 223 | ext_modules = [librabbitmq_ext] 224 | cmdclass = {'build': build} 225 | packages = find_packages(exclude=['ez_setup', 'tests', 'tests.*']) 226 | 227 | if not goahead: 228 | ext_modules = [] 229 | cmdclass = {} 230 | packages = [] 231 | 232 | 233 | # 'install doesn't always call build for some reason 234 | if 'install' in sys.argv and 'build' not in sys.argv: 235 | _index = sys.argv.index('install') 236 | sys.argv[:] = ( 237 | sys.argv[:_index] + ['build', 'install'] + sys.argv[_index + 1:] 238 | ) 239 | 240 | # 'bdist_wheel doesn't always call build for some reason 241 | if 'bdist_wheel' in sys.argv and 'build' not in sys.argv: 242 | _index = sys.argv.index('bdist_wheel') 243 | sys.argv[:] = ( 244 | sys.argv[:_index] + ['build', 'bdist_wheel'] + sys.argv[_index + 1:] 245 | ) 246 | 247 | # 'bdist_egg doesn't always call build for some reason 248 | if 'bdist_egg' in sys.argv and 'build' not in sys.argv: 249 | _index = sys.argv.index('bdist_egg') 250 | sys.argv[:] = ( 251 | sys.argv[:_index] + ['build', 'bdist_egg'] + sys.argv[_index + 1:] 252 | ) 253 | 254 | # 'test doesn't always call build for some reason 255 | if 'test' in sys.argv and 'build' not in sys.argv: 256 | _index = sys.argv.index('test') 257 | sys.argv[:] = ( 258 | sys.argv[:_index] + ['build', 'test'] + sys.argv[_index + 1:] 259 | ) 260 | 261 | setup( 262 | name='librabbitmq', 263 | version=version, 264 | url=homepage, 265 | author=author, 266 | author_email=contact, 267 | license='MPL', 268 | description='AMQP Client using the rabbitmq-c library.', 269 | long_description=long_description, 270 | test_suite="tests", 271 | zip_safe=False, 272 | packages=packages, 273 | cmdclass=cmdclass, 274 | install_requires=[ 275 | 'amqp>=1.4.6', 276 | 'six>=1.0.0', 277 | ], 278 | ext_modules=ext_modules, 279 | classifiers=[ 280 | 'Development Status :: 5 - Production/Stable', 281 | 'Operating System :: POSIX', 282 | 'Operating System :: Microsoft :: Windows', 283 | 'Programming Language :: C', 284 | 'Programming Language :: Python :: 2.7', 285 | 'Programming Language :: Python :: 3.4', 286 | 'Programming Language :: Python :: 3.5', 287 | 'Programming Language :: Python :: 3.6', 288 | 'Programming Language :: Python :: Implementation :: CPython', 289 | 'Intended Audience :: Developers', 290 | 'License :: OSI Approved :: Mozilla Public License 1.0 (MPL)', 291 | 'Topic :: Communications', 292 | 'Topic :: System :: Networking', 293 | 'Topic :: Software Development :: Libraries', 294 | ], 295 | ) 296 | -------------------------------------------------------------------------------- /Changelog: -------------------------------------------------------------------------------- 1 | .. _changelog: 2 | 3 | ================ 4 | Change history 5 | ================ 6 | 7 | .. contents:: 8 | :local: 9 | 10 | Next Release 11 | ============ 12 | 13 | - Add support for ``Connection.connect_timeout`` parameter 14 | 15 | 16 | .. _version-2.0.0: 17 | 18 | 2.0.0 19 | ===== 20 | :release-date: 2018-01-19 21 | 22 | - rabbitmq-c submodule is moved from https://github.com/ask/rabbitmq-c.git to official repository 23 | https://github.com/alanxz/rabbitmq-c and version is bumped to version 0.8.0 (caad0ef1533783729c7644a226c989c79b4c497b) 24 | 25 | - rabbitmq-codegen submodule is removed 26 | 27 | - Added support of cPython 3.4, 3.5, 3.6 28 | 29 | - Dropped support of cPython <= 2.6 30 | 31 | - librabbitmq is calling callback function with payload as memoryview instead of old style buffer 32 | 33 | - Library six is added as a requirement 34 | 35 | - Updated unittests and benchmark. Removed dependency to nose library. 36 | 37 | - AMQP client properties are exposed when connecting to RMQ broker 38 | 39 | - Empty message bodies are allowed 40 | 41 | - In recv, make sure all frames are read from the same channel 42 | 43 | 44 | 1.6.1 45 | ===== 46 | :release-date: 2014-11-17 02:45 P.M UTC 47 | 48 | - ``setup.py install`` is now forced to always call ``setup.py build`` first. 49 | 50 | 51 | .. _version-1.6.0: 52 | 53 | 1.6.0 54 | ===== 55 | :release-date: 2014-11-17 02:00 P.M UTC 56 | 57 | - Bundles ``rabbitmq-c`` 0.5.3dev (185ce081e3efc846b476995b7da7297bb0eec82c) 58 | 59 | - Bundles ``rabbitmq-codegen`` 3.4.1. 60 | 61 | - Now builds on macOS Yosemite. 62 | 63 | .. _version-1.5.2: 64 | 65 | 1.5.2 66 | ===== 67 | :release-date: 2014-05-28 05:00 P.M UTC 68 | 69 | - AMQP Array type now also supports tuples and other iterables. 70 | 71 | Internally the ``PyIter_*`` interface is used instead of ``PyList_*``. 72 | 73 | - AMQP arrays and tables now supports :const:`None`. 74 | 75 | - ``Channel.queue_declare`` now returns :class:`amqp.protocol.queue_declare_ok_t`. 76 | 77 | - ``drain_events`` now handles connection close and channel close frames 78 | (Issue #30). 79 | 80 | - Now using ``amqp_maybe_release_buffers_on_channel`` for channel based 81 | operations so that only the buffers related to that channel is released. 82 | 83 | .. _version-1.5.1: 84 | 85 | 1.5.1 86 | ===== 87 | :release-date: 2014-05-06 01:00 P.M UTC 88 | :release-by: Ask Solem 89 | 90 | - Fixed problem with compilation on Linux where `-lrt` is required. 91 | 92 | Fix contributed by Roger Hu. 93 | 94 | .. _version-1.5.0: 95 | 96 | 1.5.0 97 | ===== 98 | :release-date: 2014-04-14 09:00 P.M UTC 99 | :release-by: Ask Solem 100 | 101 | - Updated to use rabbitmq-c 0.5.0 102 | 103 | - Now supports ``Connection.server_properties``. 104 | 105 | - Now compiles on LLVM/clang and macOS Mavericks by removing the 106 | ``--mno-fused-madd`` option set by some Python installations. 107 | 108 | - Fixes possible memory leak in ``basic_publish`` (Issue #38). 109 | 110 | - ``Connection.fileno()`` now raises ``ValueError`` if the connection is 111 | disconnected (Issue #39). 112 | 113 | - ``exchange_delete`` now ignores ``nowait`` argument. 114 | 115 | .. _version-1.0.3: 116 | 117 | 1.0.3 118 | ===== 119 | :release-date: 2013-11-15 11:00 P.M UTC 120 | 121 | - Fixed installation problem on macOS 10.9 122 | 123 | .. _version-1.0.2: 124 | 125 | 1.0.2 126 | ===== 127 | :release-date: 2013-10-25 11:00 P.M UTC 128 | 129 | - "Bad Frame Read" is now ConnectionError, not ChannelError. 130 | 131 | - Fixed problem with basic_cancel when disconnected. 132 | 133 | - Fixed typo Channel.recover -> Channel.basic_recover 134 | 135 | .. _version-1.0.1: 136 | 137 | 1.0.1 138 | ===== 139 | :release-date: 2013-01-14 02:10 P.M UTC 140 | 141 | - ``queue_unbind`` argument ``binding_key`` renamed to ``routing_key`` 142 | (Issue #16). 143 | 144 | This change ensures compatibility with :mod:`amqp`. 145 | 146 | - Fixed memory leak caused by double call to ``AMQPTable_toPyDict`` 147 | (Issue #15). 148 | 149 | Fix contributed by Eric Siegel. 150 | 151 | - Fixed several potential memory leaks (Issue #15). 152 | 153 | Fix contributed by Marcus Cobden. 154 | 155 | - Removed `-ansi` compile option (Issue #17). 156 | 157 | .. _version-1.0.0: 158 | 159 | 1.0.0 160 | ===== 161 | :release-date: 2012-11-02 5:00 P.M UTC 162 | 163 | - A channel exception will now cause the channel to be revived instead of 164 | simply closing the connection. 165 | 166 | - Support for lists and dictionaries in header values. 167 | 168 | This also means that the RabbitMQ `Bcc` and `CC` header extensions now can 169 | be constructured. 170 | 171 | Contributed by C Anthony Risinger. 172 | 173 | - Support for float/double in header values. 174 | 175 | - Adds dummy ``nowait`` argument to ``queue_delete`` for amqplib API 176 | compatibility (it has no effect). 177 | 178 | - Library errors now use the correct error strings (Issue #7). 179 | 180 | .. _version-0.9.9: 181 | 182 | 0.9.9 183 | ===== 184 | :release-date: 2012-07-23 08:50 P.M BST 185 | 186 | - More llvm-gcc fixes for macOS. 187 | 188 | .. _version-0.9.8: 189 | 190 | 0.9.8 191 | ===== 192 | :release-date: 2012-07-23 07:10 P.M BST 193 | 194 | - Fixes bug when compiled using :program:`llvm-gcc` on macOS (Issue #5). 195 | 196 | .. _version-0.9.7: 197 | 198 | 0.9.7 199 | ===== 200 | :release-date: 2012-07-23 02:30 P.M BST 201 | 202 | - Connection and channel errors now close the connection, 203 | so that connection/channel cannot be used. 204 | 205 | Before an operation on a connection/channel that previously raised 206 | an exception would just hang. 207 | 208 | In the future it may be possible to have channel errors only close 209 | the channel, but this keeps it simple for now (and it works like amqplib). 210 | 211 | .. _version-0.9.6: 212 | 213 | 0.9.6 214 | ===== 215 | :release-date: 2012-06-13 11:25 P.M BST 216 | 217 | - Fixes problem with basic_publish and kombu. 218 | 219 | .. _version-0.9.5: 220 | 221 | 0.9.5 222 | ===== 223 | :release-date: 2012-06-07 12:20 P.M BST 224 | 225 | - Now supports 'arguments' for: 226 | 227 | - basic_consume 228 | - exchange_declare 229 | - queue_declare 230 | - queue_bind 231 | - queue_unbind 232 | 233 | - Release buffers after every command, so that memory 234 | does not grow on operations like ``basic_cancel``. 235 | 236 | - Now uses buffer instead of PyString_Join. 237 | 238 | - basic_consume now returns consumer tag. 239 | 240 | - Now compiles with -Wall -ansi -pedantic. 241 | 242 | - The message callback is now called from the C stack, 243 | so that drain_events does not return control back to the Python interpreter. 244 | 245 | Calling the callback from the same stack as slurping the socket enables 246 | us to pass the memory directly to the callback as a buffer object. 247 | 248 | - When receiving messages we now only allocate memory once for single frame 249 | payloads. 250 | 251 | For multiple frames we no longer create temporary PyString's, 252 | but instead just memcpy the contents to a single string buffer. 253 | 254 | - Adds amqp methods: 255 | 256 | - basic_cancel 257 | - flow 258 | - recover 259 | 260 | - Now using array.array to keep track of unused channel id's. 261 | 262 | - Internally the C Connection object now consistently have the channel 263 | as the first argument for all channel related methods. 264 | 265 | - Fixed compiler warning in basic.qos 266 | 267 | - frame_max, channel_max and heartbeat values are now updated 268 | to the values received from connection_tune_ok. 269 | 270 | - basic_recv now raises socket.timeout directly, so that don't 271 | have to reraise. 272 | 273 | - Unicode support. 274 | 275 | .. _version-0.9.3: 276 | 277 | 0.9.3 278 | ===== 279 | :release-date: 2012-05-22 06:40 P.M BST 280 | 281 | - Patches memory leak. 282 | 283 | - Use ``_PyString_Join`` to concat body parts. 284 | 285 | .. _version-0.9.2: 286 | 287 | 0.9.2 288 | ===== 289 | :release-date: 2012-05-21 09:00 P.M BST 290 | :by: Ask Solem 291 | 292 | - Fixes installation problem. 293 | 294 | .. _version-0.9.1: 295 | 296 | 0.9.1 297 | ===== 298 | :release-date: 2012-05-21 08:00 P.M BST 299 | :by: Ask Solem 300 | 301 | - Fixes installation problem. 302 | 303 | .. _version-0.9.0: 304 | 305 | 0.9.0 306 | ===== 307 | :release-date: 2012-05-21 05:30 P.M BST 308 | :by: Ask Solem 309 | 310 | - This project has been renamed to **librabbitmq** 311 | 312 | - New download url: http://pypi.python.org/pypi/librabbitmq/ 313 | 314 | - New repository url: http://github.com/celery/librabbitmq/ 315 | 316 | - No longer depends on rabbitmq-c as it is included and built with 317 | this distribution. 318 | 319 | - Now supports non-blocking I/O: 320 | 321 | - New method: ``Connection.fileno()`` 322 | 323 | - drain_events(timeout=0.0) is now non-blocking. 324 | 325 | - Fixed typo noack -> no_ack 326 | 327 | - Do not select connection if frames are already enqueued. 328 | 329 | - Now supports queue_delete and exchange_delete. 330 | 331 | .. _version-0.5.0: 332 | 333 | 0.5.0 334 | ===== 335 | :release-date: 2011-12-07 05:00 P.M GMT 336 | :by: Ask Solem 337 | 338 | - Adds header support. 339 | 340 | Application headers can be added to ``properties["headers"]``. 341 | 342 | - Sending messages: 343 | 344 | - Supported 345 | - int 346 | - str (not Unicode). 347 | 348 | - Unsupported 349 | - everything else. (see read). 350 | 351 | - Receiving messages: 352 | 353 | - Supported 354 | - boolean: bool 355 | - signed int: i8, i16, i32, i64 356 | - unsigned int: u8, u16, u32, u64 357 | - float: f32 358 | - double: f64 359 | - string: bytes 360 | 361 | - Unsupported 362 | - array 363 | - timestamp 364 | - table 365 | - void 366 | - decimal 367 | 368 | .. _version-0.4.1: 369 | 370 | 0.4.1 371 | ===== 372 | :release-date: 2011-09-27 14:00 P.M BST 373 | :by: Ask Solem 374 | 375 | - The GIL was not released while waiting for events. 376 | 377 | Fix contributed by Andrew Snowden. 378 | 379 | - ``drain_events`` would wait for socket events, even though 380 | data was already buffered in client. 381 | 382 | Fix contributed by Andrew Snowden. 383 | 384 | .. _version-0.4.0: 385 | 386 | 0.4.0 387 | ===== 388 | :release-date: 2011-09-02 18:00 P.M BST 389 | 390 | - Adds timeout support to `drain_events`. 391 | 392 | Timeout in seconds as a float, or :const:`None` for no timeout. 393 | 394 | .. _version-0.3.0: 395 | 396 | 0.3.0 397 | ===== 398 | :release-date: 2011-07-28 04:00 P.M BST 399 | 400 | - Adds support for ``basic_reject``, and ``basic_qos`` (Issue #1). 401 | 402 | .. _version-0.0.1: 403 | 404 | 0.0.1 405 | ===== 406 | :release-date: NOT RELEASED 407 | :branch: master 408 | 409 | - Initial release 410 | -------------------------------------------------------------------------------- /tests/test_functional.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import absolute_import 3 | 4 | from six.moves import xrange 5 | 6 | import socket 7 | import unittest 8 | from array import array 9 | import time 10 | 11 | from librabbitmq import Message, Connection, ConnectionError, ChannelError 12 | TEST_QUEUE = 'pyrabbit.testq' 13 | 14 | 15 | class test_Connection(unittest.TestCase): 16 | def test_connection_defaults(self): 17 | """Test making a connection with the default settings.""" 18 | with Connection() as connection: 19 | self.assertGreaterEqual(connection.fileno(), 0) 20 | 21 | def test_connection_invalid_host(self): 22 | """Test connection to an invalid host fails.""" 23 | # Will fail quickly as OS will reject it. 24 | with self.assertRaises(ConnectionError): 25 | Connection(host="255.255.255.255") 26 | 27 | def test_connection_invalid_port(self): 28 | """Test connection to an invalid port fails.""" 29 | # Will fail quickly as OS will reject it. 30 | with self.assertRaises(ConnectionError): 31 | Connection(port=0) 32 | 33 | def test_connection_timeout(self): 34 | """Test connection timeout.""" 35 | # Can't rely on local OS being configured to ignore SYN packets 36 | # (OS would normally reply with RST to closed port). To test the 37 | # timeout, need to connect to something that is either slow, or 38 | # never responds. 39 | start_time = time.time() 40 | with self.assertRaises(ConnectionError): 41 | Connection(host="google.com", port=81, connect_timeout=3) 42 | took_time = time.time() - start_time 43 | # Allow some leaway to avoid spurious test failures. 44 | self.assertGreaterEqual(took_time, 2) 45 | self.assertLessEqual(took_time, 4) 46 | 47 | def test_get_free_channel_id(self): 48 | with Connection() as connection: 49 | assert connection._get_free_channel_id() == 1 50 | assert connection._get_free_channel_id() == 2 51 | 52 | def test_get_free_channel_id__channels_full(self): 53 | with Connection() as connection: 54 | for _ in range(connection.channel_max): 55 | connection._get_free_channel_id() 56 | with self.assertRaises(ConnectionError): 57 | connection._get_free_channel_id() 58 | 59 | def test_channel(self): 60 | with Connection() as connection: 61 | self.assertEqual(connection._used_channel_ids, array('H')) 62 | connection.channel() 63 | self.assertEqual(connection._used_channel_ids, array('H', (1,))) 64 | 65 | 66 | class test_Channel(unittest.TestCase): 67 | 68 | def setUp(self): 69 | self.connection = Connection(host='localhost:5672', userid='guest', 70 | password='guest', virtual_host='/') 71 | self.channel = self.connection.channel() 72 | self.channel.queue_delete(TEST_QUEUE) 73 | self._queue_declare() 74 | 75 | def test_send_message(self): 76 | message = Message( 77 | channel=self.channel, 78 | body='the quick brown fox jumps over the lazy dog', 79 | properties=dict(content_type='application/json', 80 | content_encoding='utf-8')) 81 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 82 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 83 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 84 | self.assertGreater(self.channel.queue_purge(TEST_QUEUE), 2) 85 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 86 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 87 | 88 | def test_nonascii_headers(self): 89 | message = Message( 90 | channel=self.channel, 91 | body='the quick brown fox jumps over the lazy dog', 92 | properties=dict(content_type='application/json', 93 | content_encoding='utf-8', 94 | headers={'key': r'¯\_(ツ)_/¯'})) 95 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 96 | 97 | def _queue_declare(self): 98 | self.channel.exchange_declare(TEST_QUEUE, 'direct') 99 | x = self.channel.queue_declare(TEST_QUEUE) 100 | self.assertEqual(x.message_count, x[1]) 101 | self.assertEqual(x.consumer_count, x[2]) 102 | self.assertEqual(x.queue, TEST_QUEUE) 103 | self.channel.queue_bind(TEST_QUEUE, TEST_QUEUE, TEST_QUEUE) 104 | 105 | def test_basic_get_ack(self): 106 | message = Message( 107 | channel=self.channel, 108 | body='the quick brown fox jumps over the lazy dog', 109 | properties=dict(content_type='application/json', 110 | content_encoding='utf-8')) 111 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 112 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 113 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 114 | while True: 115 | x = self.channel.basic_get(TEST_QUEUE) 116 | if x: 117 | break 118 | self.assertIs(self.channel, x.channel) 119 | self.assertIn('message_count', x.delivery_info) 120 | self.assertIn('redelivered', x.delivery_info) 121 | self.assertEqual(x.delivery_info['routing_key'], TEST_QUEUE) 122 | self.assertEqual(x.delivery_info['exchange'], TEST_QUEUE) 123 | self.assertTrue(x.delivery_info['delivery_tag']) 124 | self.assertTrue(x.properties['content_type']) 125 | self.assertTrue(x.body) 126 | x.ack() 127 | 128 | def test_timeout_burst(self): 129 | """Check that if we have a large burst of messages in our queue 130 | that we can fetch them with a timeout without needing to receive 131 | any more messages.""" 132 | 133 | message = Message( 134 | channel=self.channel, 135 | body='the quick brown fox jumps over the lazy dog', 136 | properties=dict(content_type='application/json', 137 | content_encoding='utf-8')) 138 | 139 | for i in xrange(100): 140 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 141 | 142 | messages = [] 143 | 144 | def cb(x): 145 | messages.append(x) 146 | x.ack() 147 | 148 | self.channel.basic_consume(TEST_QUEUE, callback=cb) 149 | for i in xrange(100): 150 | self.connection.drain_events(timeout=0.2) 151 | 152 | self.assertEqual(len(messages), 100) 153 | 154 | def test_timeout(self): 155 | """Check that our ``drain_events`` call actually times out if 156 | there are no messages.""" 157 | message = Message( 158 | channel=self.channel, 159 | body='the quick brown fox jumps over the lazy dog', 160 | properties=dict(content_type='application/json', 161 | content_encoding='utf-8')) 162 | 163 | self.channel.basic_publish(message, TEST_QUEUE, TEST_QUEUE) 164 | 165 | messages = [] 166 | 167 | def cb(x): 168 | messages.append(x) 169 | x.ack() 170 | 171 | self.channel.basic_consume(TEST_QUEUE, callback=cb) 172 | self.connection.drain_events(timeout=0.1) 173 | 174 | with self.assertRaises(socket.timeout): 175 | self.connection.drain_events(timeout=0.1) 176 | self.assertEqual(len(messages), 1) 177 | 178 | def test_close(self): 179 | self.assertEqual(self.connection._used_channel_ids, array('H', (1,))) 180 | self.channel.close() 181 | self.channel = None 182 | self.assertEqual(self.connection._used_channel_ids, array('H')) 183 | 184 | def tearDown(self): 185 | if self.channel and self.connection.connected: 186 | self.channel.queue_purge(TEST_QUEUE) 187 | self.channel.close() 188 | if self.connection: 189 | try: 190 | self.connection.close() 191 | except ConnectionError: 192 | pass 193 | 194 | 195 | class test_Delete(unittest.TestCase): 196 | 197 | def setUp(self): 198 | self.connection = Connection(host='localhost:5672', userid='guest', 199 | password='guest', virtual_host='/') 200 | self.channel = self.connection.channel() 201 | self.TEST_QUEUE = 'pyrabbitmq.testq2' 202 | self.channel.queue_delete(self.TEST_QUEUE) 203 | 204 | def test_delete(self): 205 | """Test that we can declare a channel delete it, and then declare with 206 | different properties""" 207 | 208 | self.channel.exchange_declare(self.TEST_QUEUE, 'direct') 209 | self.channel.queue_declare(self.TEST_QUEUE) 210 | self.channel.queue_bind( 211 | self.TEST_QUEUE, self.TEST_QUEUE, self.TEST_QUEUE, 212 | ) 213 | 214 | # Delete the queue 215 | self.channel.queue_delete(self.TEST_QUEUE) 216 | 217 | # Declare it again 218 | x = self.channel.queue_declare(self.TEST_QUEUE, durable=True) 219 | self.assertEqual(x.queue, self.TEST_QUEUE) 220 | 221 | self.channel.queue_delete(self.TEST_QUEUE) 222 | 223 | def test_delete_empty(self): 224 | """Test that the queue doesn't get deleted if it is not empty""" 225 | self.channel.exchange_declare(self.TEST_QUEUE, 'direct') 226 | self.channel.queue_declare(self.TEST_QUEUE) 227 | self.channel.queue_bind(self.TEST_QUEUE, self.TEST_QUEUE, 228 | self.TEST_QUEUE) 229 | 230 | message = Message( 231 | channel=self.channel, 232 | body='the quick brown fox jumps over the lazy dog', 233 | properties=dict(content_type='application/json', 234 | content_encoding='utf-8')) 235 | 236 | self.channel.basic_publish(message, self.TEST_QUEUE, self.TEST_QUEUE) 237 | 238 | with self.assertRaises(ChannelError): 239 | self.channel.queue_delete(self.TEST_QUEUE, if_empty=True) 240 | 241 | # We need to make a new channel after a ChannelError 242 | self.channel = self.connection.channel() 243 | 244 | x = self.channel.basic_get(self.TEST_QUEUE) 245 | self.assertTrue(x.body) 246 | 247 | self.channel.queue_delete(self.TEST_QUEUE, if_empty=True) 248 | 249 | def tearDown(self): 250 | if self.channel and self.connection.connected: 251 | self.channel.queue_purge(TEST_QUEUE) 252 | self.channel.close() 253 | if self.connection: 254 | try: 255 | self.connection.close() 256 | except ConnectionError: 257 | pass 258 | -------------------------------------------------------------------------------- /Modules/_librabbitmq/connection.h: -------------------------------------------------------------------------------- 1 | #ifndef __PYLIBRABBIT_CONNECTION_H__ 2 | #define __PYLIBRABBIT_CONNECTION_H__ 3 | 4 | #define PY_SSIZE_T_CLEAN 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #if PY_MAJOR_VERSION == 2 12 | # define TP_FLAGS (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS) 13 | #else 14 | # define TP_FLAGS (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE) 15 | #endif 16 | 17 | #if PY_MAJOR_VERSION >= 3 18 | #define PYRABBITMQ_MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) 19 | #else 20 | #define PYRABBITMQ_MOD_INIT(name) PyMODINIT_FUNC init##name(void) 21 | #endif 22 | 23 | 24 | #if PY_VERSION_HEX >= 0x03000000 /* 3.0 and up */ 25 | # define BUILD_METHOD_NAME PyUnicode_FromString 26 | # define FROM_FORMAT PyUnicode_FromFormat 27 | # define PyInt_FromLong PyLong_FromLong 28 | # define PyInt_AS_LONG PyLong_AsLong 29 | # define PyInt_Check PyLong_Check 30 | # define PyInt_FromSsize_t PyLong_FromSsize_t 31 | # define PyString_INTERN_FROM_STRING PyString_FromString 32 | #else /* 2.x */ 33 | # define BUILD_METHOD_NAME PyBytes_FromString 34 | # define FROM_FORMAT PyString_FromFormat 35 | # define PyString_INTERN_FROM_STRING PyString_InternFromString 36 | #endif 37 | 38 | #if defined __GNUC__ && !defined __GNUC_STDC_INLINE__ && !defined __GNUC_GNU_INLINE__ 39 | # define __GNUC_GNU_INLINE__ 1 40 | #endif 41 | 42 | #ifndef _PYRMQ_INLINE 43 | # if defined(__llvm__) 44 | /* extern inline breaks on LLVM saying 'symbol not found' 45 | * but the inline keyword is only treated as a "mild hint" 46 | * to the LLVM optimizer anyway, so we'll just disable it. 47 | */ 48 | # define _PYRMQ_INLINE 49 | # elif defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__) 50 | # define _PYRMQ_INLINE extern __inline 51 | # else 52 | # define _PYRMQ_INLINE extern __inline 53 | # endif 54 | #endif 55 | 56 | 57 | _PYRMQ_INLINE PyObject* 58 | buffer_toMemoryView(char *buf, Py_ssize_t buf_len) { 59 | PyObject *view; 60 | #if PY_MAJOR_VERSION == 2 61 | PyObject *pybuffer; 62 | pybuffer = PyBuffer_FromMemory(buf, buf_len); 63 | view = PyMemoryView_FromObject(pybuffer); 64 | Py_XDECREF(pybuffer); 65 | #else 66 | view = PyMemoryView_FromMemory(buf, buf_len, PyBUF_READ); 67 | #endif 68 | return view; 69 | } 70 | 71 | #define PyDICT_SETNONE_DECREF(dict, key) \ 72 | do { \ 73 | PyDict_SetItem(dict, key, Py_None); \ 74 | Py_XDECREF(key); \ 75 | } while(0) 76 | 77 | #define PyDICT_SETSTR_DECREF(dict, key, value) \ 78 | do { \ 79 | PyDict_SetItemString(dict, key, value); \ 80 | Py_DECREF(value); \ 81 | } while (0) 82 | 83 | #define PyDICT_SETKV_DECREF(dict, key, value) \ 84 | do { \ 85 | PyDict_SetItem(dict, key, value); \ 86 | Py_XDECREF(key); \ 87 | Py_XDECREF(value); \ 88 | } while(0) 89 | 90 | #if PY_MAJOR_VERSION == 2 91 | # define PySTRING_FROM_AMQBYTES(member) \ 92 | PyString_FromStringAndSize((member).bytes, (Py_ssize_t)(member).len) 93 | #else 94 | # define PySTRING_FROM_AMQBYTES(member) \ 95 | PyUnicode_FromStringAndSize((member).bytes, (Py_ssize_t)(member).len) 96 | #endif 97 | 98 | 99 | #define AMQTable_TO_PYKEY(table, i) \ 100 | PySTRING_FROM_AMQBYTES(table->entries[i].key) 101 | 102 | #define PyDICT_SETKEY_AMQTABLE(dict, k, v, table, stmt) \ 103 | PyDICT_SETSTRKEY_DECREF(dict, k, v, \ 104 | PySTRING_FROM_AMQBYTES(table->headers.entries[i].key), \ 105 | stmt); \ 106 | 107 | #if defined(__C99__) || defined(__GNUC__) 108 | # define PyString_AS_AMQBYTES(s) \ 109 | (amqp_bytes_t){Py_SIZE(s), (void *)PyBytes_AS_STRING(s)} 110 | #else 111 | _PYRMQ_INLINE amqp_bytes_t PyString_AS_AMQBYTES(PyObject *); 112 | _PYRMQ_INLINE amqp_bytes_t 113 | PyString_AS_AMQBYTES(PyObject *s) 114 | { 115 | amqp_bytes_t ret; 116 | ret.len = Py_SIZE(s); 117 | ret.bytes = (void *)PyBytes_AS_STRING(s); 118 | /*{Py_SIZE(s), (void *)PyString_AS_STRING(s)};*/ 119 | return ret; 120 | } 121 | #endif 122 | 123 | #define PYRMQ_IS_TIMEOUT(t) (t > 0.0) 124 | #define PYRMQ_IS_NONBLOCK(t) (t == -1) 125 | #define PYRMQ_SHOULD_POLL(t) (PYRMQ_IS_TIMEOUT(t) || PYRMQ_IS_NONBLOCK(t)) 126 | 127 | #define RabbitMQ_WAIT(sockfd, timeout) \ 128 | (PYRMQ_IS_TIMEOUT(timeout) \ 129 | ? RabbitMQ_wait_timeout(sockfd, timeout) \ 130 | : RabbitMQ_wait_nb(sockfd)) 131 | 132 | #define AMQTable_VAL(table, index, typ) \ 133 | table->entries[index].value.value.typ 134 | 135 | #define AMQArray_VAL(array, index, typ) \ 136 | array->entries[index].value.typ 137 | 138 | #define AMQP_ACTIVE_BUFFERS(state) \ 139 | (amqp_data_in_buffer(state) || amqp_frames_enqueued(state)) 140 | 141 | 142 | /* Connection object */ 143 | typedef struct { 144 | PyObject_HEAD 145 | amqp_connection_state_t conn; 146 | char *hostname; 147 | char *userid; 148 | char *password; 149 | char *virtual_host; 150 | int port; 151 | int frame_max; 152 | int channel_max; 153 | int heartbeat; 154 | int connect_timeout; 155 | 156 | int sockfd; 157 | int connected; 158 | 159 | PyObject *client_properties; 160 | PyObject *server_properties; 161 | PyObject *callbacks; /* {channel_id: {consumer_tag:callback}} */ 162 | 163 | PyObject *weakreflist; 164 | } PyRabbitMQ_Connection; 165 | 166 | /* Connection method sigs */ 167 | static PyRabbitMQ_Connection* 168 | PyRabbitMQ_ConnectionType_new(PyTypeObject *, PyObject *, PyObject *); 169 | 170 | static void 171 | PyRabbitMQ_ConnectionType_dealloc(PyRabbitMQ_Connection *); 172 | 173 | static int 174 | PyRabbitMQ_ConnectionType_init(PyRabbitMQ_Connection *, 175 | PyObject *, PyObject *); 176 | 177 | static PyObject* 178 | PyRabbitMQ_Connection_fileno(PyRabbitMQ_Connection *); 179 | 180 | static PyObject* 181 | PyRabbitMQ_Connection_connect(PyRabbitMQ_Connection *); 182 | 183 | static PyObject* 184 | PyRabbitMQ_Connection_close(PyRabbitMQ_Connection *); 185 | 186 | static PyObject* 187 | PyRabbitMQ_Connection_channel_open(PyRabbitMQ_Connection *, PyObject *); 188 | 189 | static PyObject* 190 | PyRabbitMQ_Connection_channel_close(PyRabbitMQ_Connection *, PyObject *); 191 | 192 | static PyObject* 193 | PyRabbitMQ_Connection_basic_publish(PyRabbitMQ_Connection *, PyObject *); 194 | 195 | static PyObject* 196 | PyRabbitMQ_Connection_exchange_declare(PyRabbitMQ_Connection *, PyObject *); 197 | 198 | static PyObject* 199 | PyRabbitMQ_Connection_exchange_delete(PyRabbitMQ_Connection *, PyObject *); 200 | 201 | static PyObject* 202 | PyRabbitMQ_Connection_queue_delete(PyRabbitMQ_Connection *, PyObject *); 203 | 204 | static PyObject* 205 | PyRabbitMQ_Connection_queue_declare(PyRabbitMQ_Connection *, PyObject *); 206 | 207 | static PyObject* 208 | PyRabbitMQ_Connection_repr(PyRabbitMQ_Connection *); 209 | 210 | static PyObject* 211 | PyRabbitMQ_Connection_queue_bind(PyRabbitMQ_Connection *, PyObject *); 212 | 213 | static PyObject* 214 | PyRabbitMQ_Connection_queue_unbind(PyRabbitMQ_Connection *, PyObject *); 215 | 216 | static PyObject* 217 | PyRabbitMQ_Connection_basic_get(PyRabbitMQ_Connection *, PyObject *); 218 | 219 | static PyObject* 220 | PyRabbitMQ_Connection_queue_purge(PyRabbitMQ_Connection *, PyObject *); 221 | 222 | static PyObject* 223 | PyRabbitMQ_Connection_basic_ack(PyRabbitMQ_Connection *, PyObject *); 224 | 225 | static PyObject* 226 | PyRabbitMQ_Connection_basic_qos(PyRabbitMQ_Connection *, PyObject *); 227 | 228 | static PyObject* 229 | PyRabbitMQ_Connection_basic_reject(PyRabbitMQ_Connection *, PyObject *); 230 | 231 | static PyObject* 232 | PyRabbitMQ_Connection_basic_recover(PyRabbitMQ_Connection *, PyObject *); 233 | 234 | static PyObject* 235 | PyRabbitMQ_Connection_basic_recv(PyRabbitMQ_Connection *, PyObject *); 236 | 237 | static PyObject* 238 | PyRabbitMQ_Connection_basic_consume(PyRabbitMQ_Connection *, PyObject *); 239 | 240 | static PyObject* 241 | PyRabbitMQ_Connection_basic_cancel(PyRabbitMQ_Connection *, PyObject *); 242 | 243 | static PyObject 244 | *PyRabbitMQ_Connection_flow(PyRabbitMQ_Connection *, PyObject *); 245 | 246 | 247 | /* Connection attributes */ 248 | static PyMemberDef PyRabbitMQ_ConnectionType_members[] = { 249 | {"hostname", T_STRING, 250 | offsetof(PyRabbitMQ_Connection, hostname), READONLY, NULL}, 251 | {"userid", T_STRING, 252 | offsetof(PyRabbitMQ_Connection, userid), READONLY, NULL}, 253 | {"password", T_STRING, 254 | offsetof(PyRabbitMQ_Connection, password), READONLY, NULL}, 255 | {"virtual_host", T_STRING, 256 | offsetof(PyRabbitMQ_Connection, virtual_host), READONLY, NULL}, 257 | {"port", T_INT, 258 | offsetof(PyRabbitMQ_Connection, port), READONLY, NULL}, 259 | {"heartbeat", T_INT, 260 | offsetof(PyRabbitMQ_Connection, heartbeat), READONLY, NULL}, 261 | {"server_properties", T_OBJECT_EX, 262 | offsetof(PyRabbitMQ_Connection, server_properties), READONLY, NULL}, 263 | {"connected", T_INT, 264 | offsetof(PyRabbitMQ_Connection, connected), READONLY, NULL}, 265 | {"channel_max", T_INT, 266 | offsetof(PyRabbitMQ_Connection, channel_max), READONLY, NULL}, 267 | {"frame_max", T_INT, 268 | offsetof(PyRabbitMQ_Connection, frame_max), READONLY, NULL}, 269 | {"callbacks", T_OBJECT_EX, 270 | offsetof(PyRabbitMQ_Connection, callbacks), READONLY, NULL}, 271 | {"connect_timeout", T_INT, 272 | offsetof(PyRabbitMQ_Connection, connect_timeout), READONLY, NULL}, 273 | {NULL, 0, 0, 0, NULL} /* Sentinel */ 274 | }; 275 | 276 | /* Connection methods */ 277 | static PyMethodDef PyRabbitMQ_ConnectionType_methods[] = { 278 | {"fileno", (PyCFunction)PyRabbitMQ_Connection_fileno, 279 | METH_NOARGS, "File descriptor number."}, 280 | {"connect", (PyCFunction)PyRabbitMQ_Connection_connect, 281 | METH_NOARGS, "Establish connection to the broker."}, 282 | {"_close", (PyCFunction)PyRabbitMQ_Connection_close, 283 | METH_NOARGS, "Close connection."}, 284 | {"_channel_open", (PyCFunction)PyRabbitMQ_Connection_channel_open, 285 | METH_VARARGS, "Create new channel"}, 286 | {"_channel_close", (PyCFunction)PyRabbitMQ_Connection_channel_close, 287 | METH_VARARGS, "Close channel"}, 288 | {"_basic_publish", (PyCFunction)PyRabbitMQ_Connection_basic_publish, 289 | METH_VARARGS, "Publish message"}, 290 | {"_exchange_declare", (PyCFunction)PyRabbitMQ_Connection_exchange_declare, 291 | METH_VARARGS, "Declare an exchange"}, 292 | {"_exchange_delete", (PyCFunction)PyRabbitMQ_Connection_exchange_delete, 293 | METH_VARARGS, "Delete an exchange"}, 294 | {"_queue_declare", (PyCFunction)PyRabbitMQ_Connection_queue_declare, 295 | METH_VARARGS, "Declare a queue"}, 296 | {"_queue_bind", (PyCFunction)PyRabbitMQ_Connection_queue_bind, 297 | METH_VARARGS, "Bind queue"}, 298 | {"_queue_unbind", (PyCFunction)PyRabbitMQ_Connection_queue_unbind, 299 | METH_VARARGS, "Unbind queue"}, 300 | {"_queue_delete", (PyCFunction)PyRabbitMQ_Connection_queue_delete, 301 | METH_VARARGS, "Delete queue"}, 302 | {"_basic_get", (PyCFunction)PyRabbitMQ_Connection_basic_get, 303 | METH_VARARGS, "Try to receive message synchronously."}, 304 | {"_queue_purge", (PyCFunction)PyRabbitMQ_Connection_queue_purge, 305 | METH_VARARGS, "Purge all messages from queue."}, 306 | {"_basic_ack", (PyCFunction)PyRabbitMQ_Connection_basic_ack, 307 | METH_VARARGS, "Acknowledge message."}, 308 | {"_basic_reject", (PyCFunction)PyRabbitMQ_Connection_basic_reject, 309 | METH_VARARGS, "Reject message."}, 310 | {"_basic_qos", (PyCFunction)PyRabbitMQ_Connection_basic_qos, 311 | METH_VARARGS, "Set Quality of Service settings."}, 312 | {"_flow", (PyCFunction)PyRabbitMQ_Connection_flow, 313 | METH_VARARGS, "Enable/disable channel flow."}, 314 | {"_basic_recover", (PyCFunction)PyRabbitMQ_Connection_basic_recover, 315 | METH_VARARGS, "Recover all unacked messages."}, 316 | {"_basic_recv", (PyCFunction)PyRabbitMQ_Connection_basic_recv, 317 | METH_VARARGS, "Receive events from socket."}, 318 | {"_basic_consume", (PyCFunction)PyRabbitMQ_Connection_basic_consume, 319 | METH_VARARGS, "Start consuming messages from queue."}, 320 | {"_basic_cancel", (PyCFunction)PyRabbitMQ_Connection_basic_cancel, 321 | METH_VARARGS, "Cancel consuming from a queue."}, 322 | {NULL, NULL, 0, NULL} 323 | }; 324 | 325 | /* Connection.__doc__ */ 326 | PyDoc_STRVAR(PyRabbitMQ_ConnectionType_doc, 327 | "AMQP Connection: \n\n" 328 | " Connection(hostname='localhost', userid='guest',\n" 329 | " password='guest', virtual_host'/',\n" 330 | " port=5672, channel_max=0xffff,\n" 331 | " frame_max=131072, heartbeat=0).\n\n" 332 | ); 333 | 334 | static PyTypeObject PyRabbitMQ_ConnectionType = { 335 | PyVarObject_HEAD_INIT(NULL, 0) 336 | /* tp_name */ "_librabbitmq.Connection", 337 | /* tp_basicsize */ sizeof(PyRabbitMQ_Connection), 338 | /* tp_itemsize */ 0, 339 | /* tp_dealloc */ (destructor)PyRabbitMQ_ConnectionType_dealloc, 340 | /* tp_print */ 0, 341 | /* tp_getattr */ 0, 342 | /* tp_setattr */ 0, 343 | /* tp_compare */ 0, 344 | /* tp_repr */ (reprfunc)PyRabbitMQ_Connection_repr, 345 | /* tp_as_number */ 0, 346 | /* tp_as_sequence */ 0, 347 | /* tp_as_mapping */ 0, 348 | /* tp_hash */ 0, 349 | /* tp_call */ 0, 350 | /* tp_str */ 0, 351 | /* tp_getattro */ 0, 352 | /* tp_setattro */ 0, 353 | /* tp_as_buffer */ 0, 354 | /* tp_flags */ TP_FLAGS, 355 | /* tp_doc */ PyRabbitMQ_ConnectionType_doc, 356 | /* tp_traverse */ 0, 357 | /* tp_clear */ 0, 358 | /* tp_richcompare */ 0, 359 | /* tp_weaklistoffset */ offsetof(PyRabbitMQ_Connection, weakreflist), 360 | /* tp_iter */ 0, 361 | /* tp_iternext */ 0, 362 | /* tp_methods */ PyRabbitMQ_ConnectionType_methods, 363 | /* tp_members */ PyRabbitMQ_ConnectionType_members, 364 | /* tp_getset */ 0, 365 | /* tp_base */ 0, 366 | /* tp_dict */ 0, 367 | /* tp_descr_get */ 0, 368 | /* tp_descr_set */ 0, 369 | /* tp_dictoffset */ 0, 370 | /* tp_init */ (initproc)PyRabbitMQ_ConnectionType_init, 371 | /* tp_alloc */ 0, 372 | /* tp_new */ (newfunc)PyRabbitMQ_ConnectionType_new, 373 | /* tp_free */ 0, 374 | /* tp_is_gc */ 0, 375 | /* tp_bases */ 0, 376 | /* tp_mro */ 0, 377 | /* tp_cache */ 0, 378 | /* tp_subclasses */ 0, 379 | /* tp_weaklist */ 0, 380 | /* tp_del */ 0, 381 | /* tp_version_tag */ 0x010000000, 382 | }; 383 | #endif /* __PYLIBRABBIT_CONNECTION_H__ */ 384 | -------------------------------------------------------------------------------- /LICENSE-GPL-2.0: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /LICENSE-MPL-RabbitMQ: -------------------------------------------------------------------------------- 1 | MOZILLA PUBLIC LICENSE 2 | Version 1.1 3 | 4 | --------------- 5 | 6 | 1. Definitions. 7 | 8 | 1.0.1. "Commercial Use" means distribution or otherwise making the 9 | Covered Code available to a third party. 10 | 11 | 1.1. "Contributor" means each entity that creates or contributes to 12 | the creation of Modifications. 13 | 14 | 1.2. "Contributor Version" means the combination of the Original 15 | Code, prior Modifications used by a Contributor, and the Modifications 16 | made by that particular Contributor. 17 | 18 | 1.3. "Covered Code" means the Original Code or Modifications or the 19 | combination of the Original Code and Modifications, in each case 20 | including portions thereof. 21 | 22 | 1.4. "Electronic Distribution Mechanism" means a mechanism generally 23 | accepted in the software development community for the electronic 24 | transfer of data. 25 | 26 | 1.5. "Executable" means Covered Code in any form other than Source 27 | Code. 28 | 29 | 1.6. "Initial Developer" means the individual or entity identified 30 | as the Initial Developer in the Source Code notice required by Exhibit 31 | A. 32 | 33 | 1.7. "Larger Work" means a work which combines Covered Code or 34 | portions thereof with code not governed by the terms of this License. 35 | 36 | 1.8. "License" means this document. 37 | 38 | 1.8.1. "Licensable" means having the right to grant, to the maximum 39 | extent possible, whether at the time of the initial grant or 40 | subsequently acquired, any and all of the rights conveyed herein. 41 | 42 | 1.9. "Modifications" means any addition to or deletion from the 43 | substance or structure of either the Original Code or any previous 44 | Modifications. When Covered Code is released as a series of files, a 45 | Modification is: 46 | A. Any addition to or deletion from the contents of a file 47 | containing Original Code or previous Modifications. 48 | 49 | B. Any new file that contains any part of the Original Code or 50 | previous Modifications. 51 | 52 | 1.10. "Original Code" means Source Code of computer software code 53 | which is described in the Source Code notice required by Exhibit A as 54 | Original Code, and which, at the time of its release under this 55 | License is not already Covered Code governed by this License. 56 | 57 | 1.10.1. "Patent Claims" means any patent claim(s), now owned or 58 | hereafter acquired, including without limitation, method, process, 59 | and apparatus claims, in any patent Licensable by grantor. 60 | 61 | 1.11. "Source Code" means the preferred form of the Covered Code for 62 | making modifications to it, including all modules it contains, plus 63 | any associated interface definition files, scripts used to control 64 | compilation and installation of an Executable, or source code 65 | differential comparisons against either the Original Code or another 66 | well known, available Covered Code of the Contributor's choice. The 67 | Source Code can be in a compressed or archival form, provided the 68 | appropriate decompression or de-archiving software is widely available 69 | for no charge. 70 | 71 | 1.12. "You" (or "Your") means an individual or a legal entity 72 | exercising rights under, and complying with all of the terms of, this 73 | License or a future version of this License issued under Section 6.1. 74 | For legal entities, "You" includes any entity which controls, is 75 | controlled by, or is under common control with You. For purposes of 76 | this definition, "control" means (a) the power, direct or indirect, 77 | to cause the direction or management of such entity, whether by 78 | contract or otherwise, or (b) ownership of more than fifty percent 79 | (50%) of the outstanding shares or beneficial ownership of such 80 | entity. 81 | 82 | 2. Source Code License. 83 | 84 | 2.1. The Initial Developer Grant. 85 | The Initial Developer hereby grants You a world-wide, royalty-free, 86 | non-exclusive license, subject to third party intellectual property 87 | claims: 88 | (a) under intellectual property rights (other than patent or 89 | trademark) Licensable by Initial Developer to use, reproduce, 90 | modify, display, perform, sublicense and distribute the Original 91 | Code (or portions thereof) with or without Modifications, and/or 92 | as part of a Larger Work; and 93 | 94 | (b) under Patents Claims infringed by the making, using or 95 | selling of Original Code, to make, have made, use, practice, 96 | sell, and offer for sale, and/or otherwise dispose of the 97 | Original Code (or portions thereof). 98 | 99 | (c) the licenses granted in this Section 2.1(a) and (b) are 100 | effective on the date Initial Developer first distributes 101 | Original Code under the terms of this License. 102 | 103 | (d) Notwithstanding Section 2.1(b) above, no patent license is 104 | granted: 1) for code that You delete from the Original Code; 2) 105 | separate from the Original Code; or 3) for infringements caused 106 | by: i) the modification of the Original Code or ii) the 107 | combination of the Original Code with other software or devices. 108 | 109 | 2.2. Contributor Grant. 110 | Subject to third party intellectual property claims, each Contributor 111 | hereby grants You a world-wide, royalty-free, non-exclusive license 112 | 113 | (a) under intellectual property rights (other than patent or 114 | trademark) Licensable by Contributor, to use, reproduce, modify, 115 | display, perform, sublicense and distribute the Modifications 116 | created by such Contributor (or portions thereof) either on an 117 | unmodified basis, with other Modifications, as Covered Code 118 | and/or as part of a Larger Work; and 119 | 120 | (b) under Patent Claims infringed by the making, using, or 121 | selling of Modifications made by that Contributor either alone 122 | and/or in combination with its Contributor Version (or portions 123 | of such combination), to make, use, sell, offer for sale, have 124 | made, and/or otherwise dispose of: 1) Modifications made by that 125 | Contributor (or portions thereof); and 2) the combination of 126 | Modifications made by that Contributor with its Contributor 127 | Version (or portions of such combination). 128 | 129 | (c) the licenses granted in Sections 2.2(a) and 2.2(b) are 130 | effective on the date Contributor first makes Commercial Use of 131 | the Covered Code. 132 | 133 | (d) Notwithstanding Section 2.2(b) above, no patent license is 134 | granted: 1) for any code that Contributor has deleted from the 135 | Contributor Version; 2) separate from the Contributor Version; 136 | 3) for infringements caused by: i) third party modifications of 137 | Contributor Version or ii) the combination of Modifications made 138 | by that Contributor with other software (except as part of the 139 | Contributor Version) or other devices; or 4) under Patent Claims 140 | infringed by Covered Code in the absence of Modifications made by 141 | that Contributor. 142 | 143 | 3. Distribution Obligations. 144 | 145 | 3.1. Application of License. 146 | The Modifications which You create or to which You contribute are 147 | governed by the terms of this License, including without limitation 148 | Section 2.2. The Source Code version of Covered Code may be 149 | distributed only under the terms of this License or a future version 150 | of this License released under Section 6.1, and You must include a 151 | copy of this License with every copy of the Source Code You 152 | distribute. You may not offer or impose any terms on any Source Code 153 | version that alters or restricts the applicable version of this 154 | License or the recipients' rights hereunder. However, You may include 155 | an additional document offering the additional rights described in 156 | Section 3.5. 157 | 158 | 3.2. Availability of Source Code. 159 | Any Modification which You create or to which You contribute must be 160 | made available in Source Code form under the terms of this License 161 | either on the same media as an Executable version or via an accepted 162 | Electronic Distribution Mechanism to anyone to whom you made an 163 | Executable version available; and if made available via Electronic 164 | Distribution Mechanism, must remain available for at least twelve (12) 165 | months after the date it initially became available, or at least six 166 | (6) months after a subsequent version of that particular Modification 167 | has been made available to such recipients. You are responsible for 168 | ensuring that the Source Code version remains available even if the 169 | Electronic Distribution Mechanism is maintained by a third party. 170 | 171 | 3.3. Description of Modifications. 172 | You must cause all Covered Code to which You contribute to contain a 173 | file documenting the changes You made to create that Covered Code and 174 | the date of any change. You must include a prominent statement that 175 | the Modification is derived, directly or indirectly, from Original 176 | Code provided by the Initial Developer and including the name of the 177 | Initial Developer in (a) the Source Code, and (b) in any notice in an 178 | Executable version or related documentation in which You describe the 179 | origin or ownership of the Covered Code. 180 | 181 | 3.4. Intellectual Property Matters 182 | (a) Third Party Claims. 183 | If Contributor has knowledge that a license under a third party's 184 | intellectual property rights is required to exercise the rights 185 | granted by such Contributor under Sections 2.1 or 2.2, 186 | Contributor must include a text file with the Source Code 187 | distribution titled "LEGAL" which describes the claim and the 188 | party making the claim in sufficient detail that a recipient will 189 | know whom to contact. If Contributor obtains such knowledge after 190 | the Modification is made available as described in Section 3.2, 191 | Contributor shall promptly modify the LEGAL file in all copies 192 | Contributor makes available thereafter and shall take other steps 193 | (such as notifying appropriate mailing lists or newsgroups) 194 | reasonably calculated to inform those who received the Covered 195 | Code that new knowledge has been obtained. 196 | 197 | (b) Contributor APIs. 198 | If Contributor's Modifications include an application programming 199 | interface and Contributor has knowledge of patent licenses which 200 | are reasonably necessary to implement that API, Contributor must 201 | also include this information in the LEGAL file. 202 | 203 | (c) Representations. 204 | Contributor represents that, except as disclosed pursuant to 205 | Section 3.4(a) above, Contributor believes that Contributor's 206 | Modifications are Contributor's original creation(s) and/or 207 | Contributor has sufficient rights to grant the rights conveyed by 208 | this License. 209 | 210 | 3.5. Required Notices. 211 | You must duplicate the notice in Exhibit A in each file of the Source 212 | Code. If it is not possible to put such notice in a particular Source 213 | Code file due to its structure, then You must include such notice in a 214 | location (such as a relevant directory) where a user would be likely 215 | to look for such a notice. If You created one or more Modification(s) 216 | You may add your name as a Contributor to the notice described in 217 | Exhibit A. You must also duplicate this License in any documentation 218 | for the Source Code where You describe recipients' rights or ownership 219 | rights relating to Covered Code. You may choose to offer, and to 220 | charge a fee for, warranty, support, indemnity or liability 221 | obligations to one or more recipients of Covered Code. However, You 222 | may do so only on Your own behalf, and not on behalf of the Initial 223 | Developer or any Contributor. You must make it absolutely clear than 224 | any such warranty, support, indemnity or liability obligation is 225 | offered by You alone, and You hereby agree to indemnify the Initial 226 | Developer and every Contributor for any liability incurred by the 227 | Initial Developer or such Contributor as a result of warranty, 228 | support, indemnity or liability terms You offer. 229 | 230 | 3.6. Distribution of Executable Versions. 231 | You may distribute Covered Code in Executable form only if the 232 | requirements of Section 3.1-3.5 have been met for that Covered Code, 233 | and if You include a notice stating that the Source Code version of 234 | the Covered Code is available under the terms of this License, 235 | including a description of how and where You have fulfilled the 236 | obligations of Section 3.2. The notice must be conspicuously included 237 | in any notice in an Executable version, related documentation or 238 | collateral in which You describe recipients' rights relating to the 239 | Covered Code. You may distribute the Executable version of Covered 240 | Code or ownership rights under a license of Your choice, which may 241 | contain terms different from this License, provided that You are in 242 | compliance with the terms of this License and that the license for the 243 | Executable version does not attempt to limit or alter the recipient's 244 | rights in the Source Code version from the rights set forth in this 245 | License. If You distribute the Executable version under a different 246 | license You must make it absolutely clear that any terms which differ 247 | from this License are offered by You alone, not by the Initial 248 | Developer or any Contributor. You hereby agree to indemnify the 249 | Initial Developer and every Contributor for any liability incurred by 250 | the Initial Developer or such Contributor as a result of any such 251 | terms You offer. 252 | 253 | 3.7. Larger Works. 254 | You may create a Larger Work by combining Covered Code with other code 255 | not governed by the terms of this License and distribute the Larger 256 | Work as a single product. In such a case, You must make sure the 257 | requirements of this License are fulfilled for the Covered Code. 258 | 259 | 4. Inability to Comply Due to Statute or Regulation. 260 | 261 | If it is impossible for You to comply with any of the terms of this 262 | License with respect to some or all of the Covered Code due to 263 | statute, judicial order, or regulation then You must: (a) comply with 264 | the terms of this License to the maximum extent possible; and (b) 265 | describe the limitations and the code they affect. Such description 266 | must be included in the LEGAL file described in Section 3.4 and must 267 | be included with all distributions of the Source Code. Except to the 268 | extent prohibited by statute or regulation, such description must be 269 | sufficiently detailed for a recipient of ordinary skill to be able to 270 | understand it. 271 | 272 | 5. Application of this License. 273 | 274 | This License applies to code to which the Initial Developer has 275 | attached the notice in Exhibit A and to related Covered Code. 276 | 277 | 6. Versions of the License. 278 | 279 | 6.1. New Versions. 280 | Netscape Communications Corporation ("Netscape") may publish revised 281 | and/or new versions of the License from time to time. Each version 282 | will be given a distinguishing version number. 283 | 284 | 6.2. Effect of New Versions. 285 | Once Covered Code has been published under a particular version of the 286 | License, You may always continue to use it under the terms of that 287 | version. You may also choose to use such Covered Code under the terms 288 | of any subsequent version of the License published by Netscape. No one 289 | other than Netscape has the right to modify the terms applicable to 290 | Covered Code created under this License. 291 | 292 | 6.3. Derivative Works. 293 | If You create or use a modified version of this License (which you may 294 | only do in order to apply it to code which is not already Covered Code 295 | governed by this License), You must (a) rename Your license so that 296 | the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", 297 | "MPL", "NPL" or any confusingly similar phrase do not appear in your 298 | license (except to note that your license differs from this License) 299 | and (b) otherwise make it clear that Your version of the license 300 | contains terms which differ from the Mozilla Public License and 301 | Netscape Public License. (Filling in the name of the Initial 302 | Developer, Original Code or Contributor in the notice described in 303 | Exhibit A shall not of themselves be deemed to be modifications of 304 | this License.) 305 | 306 | 7. DISCLAIMER OF WARRANTY. 307 | 308 | COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 309 | WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 310 | WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF 311 | DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. 312 | THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE 313 | IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, 314 | YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE 315 | COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER 316 | OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF 317 | ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 318 | 319 | 8. TERMINATION. 320 | 321 | 8.1. This License and the rights granted hereunder will terminate 322 | automatically if You fail to comply with terms herein and fail to cure 323 | such breach within 30 days of becoming aware of the breach. All 324 | sublicenses to the Covered Code which are properly granted shall 325 | survive any termination of this License. Provisions which, by their 326 | nature, must remain in effect beyond the termination of this License 327 | shall survive. 328 | 329 | 8.2. If You initiate litigation by asserting a patent infringement 330 | claim (excluding declatory judgment actions) against Initial Developer 331 | or a Contributor (the Initial Developer or Contributor against whom 332 | You file such action is referred to as "Participant") alleging that: 333 | 334 | (a) such Participant's Contributor Version directly or indirectly 335 | infringes any patent, then any and all rights granted by such 336 | Participant to You under Sections 2.1 and/or 2.2 of this License 337 | shall, upon 60 days notice from Participant terminate prospectively, 338 | unless if within 60 days after receipt of notice You either: (i) 339 | agree in writing to pay Participant a mutually agreeable reasonable 340 | royalty for Your past and future use of Modifications made by such 341 | Participant, or (ii) withdraw Your litigation claim with respect to 342 | the Contributor Version against such Participant. If within 60 days 343 | of notice, a reasonable royalty and payment arrangement are not 344 | mutually agreed upon in writing by the parties or the litigation claim 345 | is not withdrawn, the rights granted by Participant to You under 346 | Sections 2.1 and/or 2.2 automatically terminate at the expiration of 347 | the 60 day notice period specified above. 348 | 349 | (b) any software, hardware, or device, other than such Participant's 350 | Contributor Version, directly or indirectly infringes any patent, then 351 | any rights granted to You by such Participant under Sections 2.1(b) 352 | and 2.2(b) are revoked effective as of the date You first made, used, 353 | sold, distributed, or had made, Modifications made by that 354 | Participant. 355 | 356 | 8.3. If You assert a patent infringement claim against Participant 357 | alleging that such Participant's Contributor Version directly or 358 | indirectly infringes any patent where such claim is resolved (such as 359 | by license or settlement) prior to the initiation of patent 360 | infringement litigation, then the reasonable value of the licenses 361 | granted by such Participant under Sections 2.1 or 2.2 shall be taken 362 | into account in determining the amount or value of any payment or 363 | license. 364 | 365 | 8.4. In the event of termination under Sections 8.1 or 8.2 above, 366 | all end user license agreements (excluding distributors and resellers) 367 | which have been validly granted by You or any distributor hereunder 368 | prior to termination shall survive termination. 369 | 370 | 9. LIMITATION OF LIABILITY. 371 | 372 | UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT 373 | (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL 374 | DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, 375 | OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR 376 | ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY 377 | CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, 378 | WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER 379 | COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN 380 | INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF 381 | LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY 382 | RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW 383 | PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE 384 | EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO 385 | THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 386 | 387 | 10. U.S. GOVERNMENT END USERS. 388 | 389 | The Covered Code is a "commercial item," as that term is defined in 390 | 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer 391 | software" and "commercial computer software documentation," as such 392 | terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 393 | C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), 394 | all U.S. Government End Users acquire Covered Code with only those 395 | rights set forth herein. 396 | 397 | 11. MISCELLANEOUS. 398 | 399 | This License represents the complete agreement concerning subject 400 | matter hereof. If any provision of this License is held to be 401 | unenforceable, such provision shall be reformed only to the extent 402 | necessary to make it enforceable. This License shall be governed by 403 | California law provisions (except to the extent applicable law, if 404 | any, provides otherwise), excluding its conflict-of-law provisions. 405 | With respect to disputes in which at least one party is a citizen of, 406 | or an entity chartered or registered to do business in the United 407 | States of America, any litigation relating to this License shall be 408 | subject to the jurisdiction of the Federal Courts of the Northern 409 | District of California, with venue lying in Santa Clara County, 410 | California, with the losing party responsible for costs, including 411 | without limitation, court costs and reasonable attorneys' fees and 412 | expenses. The application of the United Nations Convention on 413 | Contracts for the International Sale of Goods is expressly excluded. 414 | Any law or regulation which provides that the language of a contract 415 | shall be construed against the drafter shall not apply to this 416 | License. 417 | 418 | 12. RESPONSIBILITY FOR CLAIMS. 419 | 420 | As between Initial Developer and the Contributors, each party is 421 | responsible for claims and damages arising, directly or indirectly, 422 | out of its utilization of rights under this License and You agree to 423 | work with Initial Developer and Contributors to distribute such 424 | responsibility on an equitable basis. Nothing herein is intended or 425 | shall be deemed to constitute any admission of liability. 426 | 427 | 13. MULTIPLE-LICENSED CODE. 428 | 429 | Initial Developer may designate portions of the Covered Code as 430 | "Multiple-Licensed". "Multiple-Licensed" means that the Initial 431 | Developer permits you to utilize portions of the Covered Code under 432 | Your choice of the NPL or the alternative licenses, if any, specified 433 | by the Initial Developer in the file described in Exhibit A. 434 | 435 | EXHIBIT A -Mozilla Public License. 436 | 437 | ``The contents of this file are subject to the Mozilla Public License 438 | Version 1.1 (the "License"); you may not use this file except in 439 | compliance with the License. You may obtain a copy of the License at 440 | http://www.mozilla.org/MPL/ 441 | 442 | Software distributed under the License is distributed on an "AS IS" 443 | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 444 | License for the specific language governing rights and limitations 445 | under the License. 446 | 447 | The Original Code is RabbitMQ. 448 | 449 | The Initial Developers of the Original Code are LShift Ltd, 450 | Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd. 451 | 452 | Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd, 453 | Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd 454 | are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial 455 | Technologies LLC, and Rabbit Technologies Ltd. 456 | 457 | Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift 458 | Ltd. Portions created by Cohesive Financial Technologies LLC are 459 | Copyright (C) 2007-2009 Cohesive Financial Technologies 460 | LLC. Portions created by Rabbit Technologies Ltd are Copyright 461 | (C) 2007-2009 Rabbit Technologies Ltd. 462 | 463 | All Rights Reserved. 464 | 465 | Contributor(s): ______________________________________.'' 466 | 467 | [NOTE: The text of this Exhibit A may differ slightly from the text of 468 | the notices in the Source Code files of the Original Code. You should 469 | use the text of this Exhibit A rather than the text found in the 470 | Original Code Source Code for Your Modifications.] 471 | -------------------------------------------------------------------------------- /Modules/_librabbitmq/connection.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "connection.h" 13 | #include "distmeta.h" 14 | 15 | #define PYRABBITMQ_CONNECTION_ERROR 0x10 16 | #define PYRABBITMQ_CHANNEL_ERROR 0x20 17 | 18 | #define PYRABBITMQ_MODULE_NAME "_librabbitmq" 19 | #define PYRABBITMQ_MODULE_DESC "Hand-made wrapper for librabbitmq." 20 | 21 | /* ------: Private Prototypes :------------------------------------------- */ 22 | PyMODINIT_FUNC init_librabbitmq(void); 23 | 24 | extern PyObject *PyRabbitMQ_socket_timeout; 25 | 26 | /* Exceptions */ 27 | PyObject *PyRabbitMQExc_ConnectionError; 28 | PyObject *PyRabbitMQExc_ChannelError; 29 | PyObject *PyRabbitMQ_socket_timeout; 30 | 31 | extern int amqp_simple_wait_frame_on_channel(amqp_connection_state_t, amqp_channel_t, amqp_frame_t *); 32 | 33 | _PYRMQ_INLINE amqp_table_entry_t* 34 | AMQTable_AddEntry(amqp_table_t*, amqp_bytes_t); 35 | 36 | _PYRMQ_INLINE void 37 | AMQTable_SetTableValue(amqp_table_t*, amqp_bytes_t, amqp_table_t); 38 | 39 | _PYRMQ_INLINE void 40 | AMQTable_SetArrayValue(amqp_table_t*, amqp_bytes_t, amqp_array_t); 41 | 42 | _PYRMQ_INLINE void 43 | AMQTable_SetStringValue(amqp_table_t*, amqp_bytes_t, amqp_bytes_t); 44 | 45 | _PYRMQ_INLINE void 46 | AMQTable_SetBoolValue(amqp_table_t*, amqp_bytes_t, int); 47 | 48 | _PYRMQ_INLINE void 49 | AMQTable_SetIntValue(amqp_table_t *, amqp_bytes_t, int); 50 | 51 | _PYRMQ_INLINE void 52 | AMQTable_SetNilValue(amqp_table_t *, amqp_bytes_t); 53 | 54 | _PYRMQ_INLINE void 55 | AMQTable_SetDoubleValue(amqp_table_t *, amqp_bytes_t, double); 56 | 57 | _PYRMQ_INLINE amqp_field_value_t* 58 | AMQArray_AddEntry(amqp_array_t*); 59 | 60 | _PYRMQ_INLINE void 61 | AMQArray_SetNilValue(amqp_array_t*); 62 | 63 | _PYRMQ_INLINE void 64 | AMQArray_SetTableValue(amqp_array_t*, amqp_table_t); 65 | 66 | _PYRMQ_INLINE void 67 | AMQArray_SetArrayValue(amqp_array_t*, amqp_array_t); 68 | 69 | _PYRMQ_INLINE void 70 | AMQArray_SetStringValue(amqp_array_t*, amqp_bytes_t); 71 | 72 | _PYRMQ_INLINE void 73 | AMQArray_SetIntValue(amqp_array_t *, int); 74 | 75 | _PYRMQ_INLINE int64_t RabbitMQ_now_usec(void); 76 | _PYRMQ_INLINE int RabbitMQ_wait_nb(int); 77 | _PYRMQ_INLINE int RabbitMQ_wait_timeout(int, double); 78 | 79 | _PYRMQ_INLINE void 80 | basic_properties_to_PyDict(amqp_basic_properties_t*, PyObject*); 81 | 82 | // Keep track of PyObject references with increased reference count 83 | // Entries are stored in fixed size array. 84 | #define PYOBJECT_ARRAY_MAX 5 85 | typedef struct pyobject_array_t { 86 | int num_entries; 87 | PyObject *entries[PYOBJECT_ARRAY_MAX]; 88 | struct pyobject_array_t *next; 89 | } pyobject_array_t; 90 | 91 | 92 | static void PyObjectArray_XDECREF(pyobject_array_t *array); 93 | 94 | _PYRMQ_INLINE PyObject* PyObjectArray_AddEntry(pyobject_array_t *, PyObject *obj); 95 | _PYRMQ_INLINE PyObject* PyObjectArray_Maybe_Unicode(PyObject *, pyobject_array_t *); 96 | 97 | 98 | static int PyDict_to_basic_properties(PyObject *, 99 | amqp_basic_properties_t *, 100 | amqp_connection_state_t, 101 | amqp_pool_t *, 102 | pyobject_array_t *); 103 | 104 | _PYRMQ_INLINE void 105 | amqp_basic_deliver_to_PyDict(PyObject *, uint64_t, amqp_bytes_t, 106 | amqp_bytes_t, amqp_boolean_t); 107 | 108 | _PYRMQ_INLINE int 109 | PyRabbitMQ_ApplyCallback(PyRabbitMQ_Connection *, 110 | PyObject *, PyObject *, PyObject *, 111 | PyObject *, PyObject *); 112 | 113 | int 114 | PyRabbitMQ_recv(PyRabbitMQ_Connection *, PyObject *, 115 | amqp_connection_state_t, int); 116 | 117 | unsigned int 118 | PyRabbitMQ_revive_channel(PyRabbitMQ_Connection *, unsigned int); 119 | 120 | unsigned int 121 | PyRabbitMQ_Connection_create_channel(PyRabbitMQ_Connection *, unsigned int); 122 | 123 | int PyRabbitMQ_HandleError(int, char const *); 124 | _PYRMQ_INLINE int PyRabbitMQ_HandlePollError(int); 125 | int PyRabbitMQ_HandleAMQError(PyRabbitMQ_Connection *, unsigned int, 126 | amqp_rpc_reply_t, const char *); 127 | void PyRabbitMQ_SetErr_UnexpectedHeader(amqp_frame_t*); 128 | int PyRabbitMQ_Not_Connected(PyRabbitMQ_Connection *); 129 | 130 | static amqp_table_t PyDict_ToAMQTable(amqp_connection_state_t, PyObject *, amqp_pool_t *, pyobject_array_t *); 131 | static amqp_array_t PyIter_ToAMQArray(amqp_connection_state_t, PyObject *, amqp_pool_t *, pyobject_array_t *); 132 | 133 | static PyObject* AMQTable_toPyDict(amqp_table_t *table); 134 | static PyObject* AMQArray_toPyList(amqp_array_t *array); 135 | 136 | int PyRabbitMQ_HandleAMQStatus(int, const char *); 137 | 138 | int PyRabbitMQ_Not_Connected(PyRabbitMQ_Connection *self) 139 | { 140 | if (!self->connected) { 141 | PyErr_SetString(PyRabbitMQExc_ConnectionError, 142 | "Operation on closed connection"); 143 | return 1; 144 | } 145 | return 0; 146 | } 147 | 148 | /* ------: AMQP Utils :--------------------------------------------------- */ 149 | 150 | _PYRMQ_INLINE amqp_table_entry_t* 151 | AMQTable_AddEntry(amqp_table_t *table, amqp_bytes_t key) 152 | { 153 | amqp_table_entry_t *entry = &table->entries[table->num_entries]; 154 | table->num_entries++; 155 | entry->key = key; 156 | return entry; 157 | } 158 | 159 | _PYRMQ_INLINE void 160 | AMQTable_SetTableValue(amqp_table_t *table, 161 | amqp_bytes_t key, amqp_table_t value) 162 | { 163 | amqp_table_entry_t *entry = AMQTable_AddEntry(table, key); 164 | entry->value.kind = AMQP_FIELD_KIND_TABLE; 165 | entry->value.value.table = value; 166 | } 167 | 168 | _PYRMQ_INLINE void 169 | AMQTable_SetArrayValue(amqp_table_t *table, 170 | amqp_bytes_t key, amqp_array_t value) 171 | { 172 | amqp_table_entry_t *entry = AMQTable_AddEntry(table, key); 173 | entry->value.kind = AMQP_FIELD_KIND_ARRAY; 174 | entry->value.value.array = value; 175 | } 176 | 177 | _PYRMQ_INLINE void 178 | AMQTable_SetStringValue(amqp_table_t *table, 179 | amqp_bytes_t key, amqp_bytes_t value) 180 | { 181 | amqp_table_entry_t *entry = AMQTable_AddEntry(table, key); 182 | entry->value.kind = AMQP_FIELD_KIND_UTF8; 183 | entry->value.value.bytes = value; 184 | } 185 | 186 | _PYRMQ_INLINE void 187 | AMQTable_SetBoolValue(amqp_table_t *table, 188 | amqp_bytes_t key, int value) 189 | { 190 | amqp_table_entry_t *entry = AMQTable_AddEntry(table, key); 191 | entry->value.kind = AMQP_FIELD_KIND_BOOLEAN; 192 | entry->value.value.boolean = value; 193 | } 194 | 195 | _PYRMQ_INLINE void 196 | AMQTable_SetIntValue(amqp_table_t *table, 197 | amqp_bytes_t key, int value) 198 | { 199 | amqp_table_entry_t *entry = AMQTable_AddEntry(table, key); 200 | entry->value.kind = AMQP_FIELD_KIND_I32; 201 | entry->value.value.i32 = value; 202 | } 203 | 204 | _PYRMQ_INLINE void 205 | AMQTable_SetNilValue(amqp_table_t *table, amqp_bytes_t key) { 206 | amqp_table_entry_t *entry = AMQTable_AddEntry(table, key); 207 | entry->value.kind = AMQP_FIELD_KIND_VOID; 208 | } 209 | 210 | _PYRMQ_INLINE void 211 | AMQTable_SetDoubleValue(amqp_table_t *table, 212 | amqp_bytes_t key, double value) 213 | { 214 | amqp_table_entry_t *entry = AMQTable_AddEntry(table, key); 215 | entry->value.kind = AMQP_FIELD_KIND_F64; 216 | entry->value.value.f64 = value; 217 | } 218 | 219 | _PYRMQ_INLINE amqp_field_value_t* 220 | AMQArray_AddEntry(amqp_array_t *array) 221 | { 222 | amqp_field_value_t *entry = &array->entries[array->num_entries]; 223 | array->num_entries++; 224 | return entry; 225 | } 226 | 227 | _PYRMQ_INLINE void 228 | AMQArray_SetNilValue(amqp_array_t *array) 229 | { 230 | amqp_field_value_t *entry = AMQArray_AddEntry(array); 231 | entry->kind = AMQP_FIELD_KIND_VOID; 232 | } 233 | 234 | _PYRMQ_INLINE void 235 | AMQArray_SetTableValue(amqp_array_t *array, amqp_table_t value) 236 | { 237 | amqp_field_value_t *entry = AMQArray_AddEntry(array); 238 | entry->kind = AMQP_FIELD_KIND_TABLE; 239 | entry->value.table = value; 240 | } 241 | 242 | _PYRMQ_INLINE void 243 | AMQArray_SetArrayValue(amqp_array_t *array, amqp_array_t value) 244 | { 245 | amqp_field_value_t *entry = AMQArray_AddEntry(array); 246 | entry->kind = AMQP_FIELD_KIND_ARRAY; 247 | entry->value.array = value; 248 | } 249 | 250 | _PYRMQ_INLINE void 251 | AMQArray_SetStringValue(amqp_array_t *array, amqp_bytes_t value) 252 | { 253 | amqp_field_value_t *entry = AMQArray_AddEntry(array); 254 | entry->kind = AMQP_FIELD_KIND_UTF8; 255 | entry->value.bytes = value; 256 | } 257 | 258 | _PYRMQ_INLINE void 259 | AMQArray_SetIntValue(amqp_array_t *array, int value) 260 | { 261 | amqp_field_value_t *entry = AMQArray_AddEntry(array); 262 | entry->kind = AMQP_FIELD_KIND_I32; 263 | entry->value.i32 = value; 264 | } 265 | 266 | static amqp_table_t 267 | PyDict_ToAMQTable(amqp_connection_state_t conn, PyObject *src, amqp_pool_t *pool, pyobject_array_t *pyobj_array) 268 | { 269 | PyObject *dkey = NULL; 270 | PyObject *dvalue = NULL; 271 | Py_ssize_t size = 0; 272 | Py_ssize_t pos = 0; 273 | uint64_t clong_value = 0; 274 | double cdouble_value = 0.0; 275 | int is_unicode = 0; 276 | amqp_table_t dst = amqp_empty_table; 277 | 278 | size = PyDict_Size(src); 279 | 280 | /* allocate new table */ 281 | dst.num_entries = 0; 282 | dst.entries = amqp_pool_alloc(pool, size * sizeof(amqp_table_entry_t)); 283 | while (PyDict_Next(src, &pos, &dkey, &dvalue)) { 284 | dkey = PyObjectArray_Maybe_Unicode(dkey, pyobj_array); 285 | 286 | if (dvalue == Py_None) { 287 | /* None */ 288 | AMQTable_SetNilValue(&dst, PyString_AS_AMQBYTES(dkey)); 289 | } 290 | else if (PyDict_Check(dvalue)) { 291 | /* Dict */ 292 | AMQTable_SetTableValue(&dst, 293 | PyString_AS_AMQBYTES(dkey), 294 | PyDict_ToAMQTable(conn, dvalue, pool, pyobj_array)); 295 | } 296 | else if (PyList_Check(dvalue) || PyTuple_Check(dvalue)) { 297 | /* List */ 298 | AMQTable_SetArrayValue(&dst, 299 | PyString_AS_AMQBYTES(dkey), 300 | PyIter_ToAMQArray(conn, dvalue, pool, pyobj_array)); 301 | } 302 | else if (PyBool_Check(dvalue)) { 303 | /* Bool */ 304 | clong_value = 0; /* default false */ 305 | 306 | if (dvalue == Py_True) 307 | clong_value = 1; 308 | 309 | AMQTable_SetBoolValue(&dst, 310 | PyString_AS_AMQBYTES(dkey), 311 | clong_value); 312 | } 313 | else if (PyLong_Check(dvalue) || PyInt_Check(dvalue)) { 314 | /* Int | Long */ 315 | clong_value = (int64_t)PyLong_AsLong(dvalue); 316 | 317 | if (clong_value == -1) 318 | goto error; 319 | 320 | AMQTable_SetIntValue(&dst, 321 | PyString_AS_AMQBYTES(dkey), 322 | clong_value 323 | ); 324 | } 325 | else if (PyFloat_Check(dvalue)) { 326 | cdouble_value = PyFloat_AsDouble(dvalue); 327 | 328 | if (cdouble_value == -1) 329 | goto error; 330 | 331 | AMQTable_SetDoubleValue(&dst, 332 | PyString_AS_AMQBYTES(dkey), 333 | cdouble_value 334 | ); 335 | } 336 | else { 337 | /* String | Unicode */ 338 | is_unicode = PyUnicode_Check(dvalue); 339 | if (is_unicode || PyBytes_Check(dvalue)) { 340 | if (is_unicode) { 341 | if ((dvalue = PyUnicode_AsEncodedString(dvalue, "utf-8", "strict")) == NULL) 342 | goto error; 343 | PyObjectArray_AddEntry(pyobj_array, dvalue); 344 | } 345 | AMQTable_SetStringValue(&dst, 346 | PyString_AS_AMQBYTES(dkey), 347 | PyString_AS_AMQBYTES(dvalue) 348 | ); 349 | } 350 | else { 351 | /* unsupported type */ 352 | PyErr_Format(PyExc_ValueError, 353 | "Table member %s is of an unsupported type", 354 | PyBytes_AS_STRING(dkey)); 355 | goto error; 356 | } 357 | } 358 | } 359 | return dst; 360 | error: 361 | assert(PyErr_Occurred()); 362 | return amqp_empty_table; 363 | } 364 | 365 | static amqp_array_t 366 | PyIter_ToAMQArray(amqp_connection_state_t conn, PyObject *src, amqp_pool_t *pool, pyobject_array_t *pyobj_array) 367 | { 368 | Py_ssize_t pos = 0; 369 | uint64_t clong_value = 0; 370 | int is_unicode = 0; 371 | amqp_array_t dst = amqp_empty_array; 372 | 373 | Py_ssize_t size = PySequence_Size(src); 374 | if (size == -1) return dst; 375 | 376 | PyObject *iterator = PyObject_GetIter(src); 377 | if (iterator == NULL) return dst; 378 | PyObject *item = NULL; 379 | PyObject *item_tmp = NULL; 380 | 381 | /* allocate new amqp array */ 382 | dst.num_entries = 0; 383 | dst.entries = amqp_pool_alloc(pool, size * sizeof(amqp_field_value_t)); 384 | 385 | while ((item = PyIter_Next(iterator))) { 386 | if (item == Py_None) { 387 | /* None */ 388 | AMQArray_SetNilValue(&dst); 389 | } 390 | else if (PyDict_Check(item)) { 391 | /* Dict */ 392 | AMQArray_SetTableValue( 393 | &dst, PyDict_ToAMQTable(conn, item, pool, pyobj_array)); 394 | } 395 | else if (PyList_Check(item) || PyTuple_Check(item)) { 396 | /* List */ 397 | AMQArray_SetArrayValue( 398 | &dst, PyIter_ToAMQArray(conn, item, pool, pyobj_array)); 399 | } 400 | else if (PyLong_Check(item) || PyInt_Check(item)) { 401 | /* Int | Long */ 402 | clong_value = (int64_t)PyLong_AsLongLong(item); 403 | AMQArray_SetIntValue(&dst, clong_value); 404 | } 405 | else { 406 | /* String | Unicode */ 407 | is_unicode = PyUnicode_Check(item); 408 | if (is_unicode || PyBytes_Check(item)) { 409 | if (is_unicode) { 410 | /* PyUnicode_AsASCIIString returns a new ref! */ 411 | item_tmp = item; 412 | if ((item = PyUnicode_AsASCIIString(item)) == NULL) 413 | goto item_error; 414 | Py_XDECREF(item_tmp); 415 | } 416 | AMQArray_SetStringValue( 417 | &dst, PyString_AS_AMQBYTES(item)); 418 | } 419 | else { 420 | /* unsupported type */ 421 | PyErr_Format(PyExc_ValueError, 422 | "Array member at index %lu, %R, is of an unsupported type", 423 | pos, item); 424 | goto item_error; 425 | } 426 | } 427 | Py_XDECREF(item); 428 | } 429 | 430 | return dst; 431 | item_error: 432 | Py_XDECREF(item_tmp); 433 | Py_XDECREF(item); 434 | error: 435 | Py_XDECREF(iterator); 436 | assert(PyErr_Occurred()); 437 | return dst; 438 | } 439 | 440 | 441 | _PYRMQ_INLINE int64_t RabbitMQ_now_usec(void) 442 | { 443 | struct timeval tv; 444 | gettimeofday(&tv, NULL); 445 | return (int64_t)tv.tv_sec * 10e5 + (int64_t)tv.tv_usec; 446 | } 447 | 448 | _PYRMQ_INLINE int RabbitMQ_wait_nb(int sockfd) 449 | { 450 | register int result = 0; 451 | fd_set fdset; 452 | struct timeval tv = {0, 0}; 453 | 454 | FD_ZERO(&fdset); 455 | FD_SET(sockfd, &fdset); 456 | 457 | result = select(sockfd + 1, &fdset, NULL, NULL, &tv); 458 | if (result <= 0) 459 | return result; 460 | if (FD_ISSET(sockfd, &fdset)) 461 | return 1; 462 | return 0; 463 | } 464 | 465 | _PYRMQ_INLINE int RabbitMQ_wait_timeout(int sockfd, double timeout) 466 | { 467 | int64_t t1, t2; 468 | register int result = 0; 469 | fd_set fdset; 470 | struct timeval tv; 471 | 472 | while (timeout > 0.0) { 473 | FD_ZERO(&fdset); 474 | FD_SET(sockfd, &fdset); 475 | tv.tv_sec = (int)timeout; 476 | tv.tv_usec = (int)((timeout - tv.tv_sec) * 1e6); 477 | 478 | t1 = RabbitMQ_now_usec(); 479 | result = select(sockfd + 1, &fdset, NULL, NULL, &tv); 480 | 481 | if (result <= 0) 482 | break; 483 | 484 | if (FD_ISSET(sockfd, &fdset)) { 485 | result = 1; 486 | break; 487 | } 488 | 489 | t2 = RabbitMQ_now_usec(); 490 | timeout -= (double)(t2 / 1e6) - (t1 / 1e6); 491 | } 492 | return result; 493 | } 494 | 495 | 496 | _PYRMQ_INLINE void 497 | basic_properties_to_PyDict(amqp_basic_properties_t *props, PyObject *p) 498 | { 499 | register PyObject *value = NULL; 500 | 501 | if (props->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { 502 | value = PySTRING_FROM_AMQBYTES(props->content_type); 503 | PyDICT_SETSTR_DECREF(p, "content_type", value); 504 | } 505 | if (props->_flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) { 506 | value = PySTRING_FROM_AMQBYTES(props->content_encoding); 507 | PyDICT_SETSTR_DECREF(p, "content_encoding", value); 508 | } 509 | if (props->_flags & AMQP_BASIC_CORRELATION_ID_FLAG) { 510 | value = PySTRING_FROM_AMQBYTES(props->correlation_id); 511 | PyDICT_SETSTR_DECREF(p, "correlation_id", value); 512 | } 513 | if (props->_flags & AMQP_BASIC_REPLY_TO_FLAG) { 514 | value = PySTRING_FROM_AMQBYTES(props->reply_to); 515 | PyDICT_SETSTR_DECREF(p, "reply_to", value); 516 | } 517 | if (props->_flags & AMQP_BASIC_EXPIRATION_FLAG) { 518 | value = PySTRING_FROM_AMQBYTES(props->expiration); 519 | PyDICT_SETSTR_DECREF(p, "expiration", value); 520 | } 521 | if (props->_flags & AMQP_BASIC_MESSAGE_ID_FLAG) { 522 | value = PySTRING_FROM_AMQBYTES(props->message_id); 523 | PyDICT_SETSTR_DECREF(p, "message_id", value); 524 | } 525 | if (props->_flags & AMQP_BASIC_TYPE_FLAG) { 526 | value = PySTRING_FROM_AMQBYTES(props->type); 527 | PyDICT_SETSTR_DECREF(p, "type", value); 528 | } 529 | if (props->_flags & AMQP_BASIC_USER_ID_FLAG) { 530 | value = PySTRING_FROM_AMQBYTES(props->user_id); 531 | PyDICT_SETSTR_DECREF(p, "user_id", value); 532 | } 533 | if (props->_flags & AMQP_BASIC_APP_ID_FLAG) { 534 | value = PySTRING_FROM_AMQBYTES(props->app_id); 535 | PyDICT_SETSTR_DECREF(p, "app_id", value); 536 | } 537 | if (props->_flags & AMQP_BASIC_DELIVERY_MODE_FLAG) { 538 | value = PyInt_FromLong(props->delivery_mode); 539 | PyDICT_SETSTR_DECREF(p, "delivery_mode", value); 540 | } 541 | if (props->_flags & AMQP_BASIC_PRIORITY_FLAG) { 542 | value =PyInt_FromLong(props->priority); 543 | PyDICT_SETSTR_DECREF(p, "priority", value); 544 | } 545 | if (props->_flags & AMQP_BASIC_TIMESTAMP_FLAG) { 546 | value = PyInt_FromLong(props->timestamp); 547 | PyDICT_SETSTR_DECREF(p, "timestamp", value); 548 | } 549 | if (props->_flags & AMQP_BASIC_HEADERS_FLAG) { 550 | value = AMQTable_toPyDict(&(props->headers)); 551 | PyDICT_SETSTR_DECREF(p, "headers", value); 552 | } 553 | } 554 | 555 | 556 | static PyObject* 557 | AMQTable_toPyDict(amqp_table_t *table) 558 | { 559 | register PyObject *key = NULL; 560 | register PyObject *value = NULL; 561 | PyObject *dict = NULL; 562 | dict = PyDict_New(); 563 | 564 | if (table) { 565 | int i; 566 | for (i = 0; i < table->num_entries; ++i, key=value=NULL) { 567 | switch (table->entries[i].value.kind) { 568 | case AMQP_FIELD_KIND_VOID: 569 | value = Py_None; 570 | break; 571 | case AMQP_FIELD_KIND_BOOLEAN: 572 | value = PyBool_FromLong(AMQTable_VAL(table, i, boolean)); 573 | break; 574 | case AMQP_FIELD_KIND_I8: 575 | value = PyInt_FromLong(AMQTable_VAL(table, i, i8)); 576 | break; 577 | case AMQP_FIELD_KIND_I16: 578 | value = PyInt_FromLong(AMQTable_VAL(table, i, i16)); 579 | break; 580 | case AMQP_FIELD_KIND_I32: 581 | value = PyInt_FromLong(AMQTable_VAL(table, i, i32)); 582 | break; 583 | case AMQP_FIELD_KIND_I64: 584 | value = PyLong_FromLong(AMQTable_VAL(table, i, i64)); 585 | break; 586 | case AMQP_FIELD_KIND_U8: 587 | value = PyLong_FromUnsignedLong( 588 | AMQTable_VAL(table, i, u8)); 589 | break; 590 | case AMQP_FIELD_KIND_U16: 591 | value = PyLong_FromUnsignedLong( 592 | AMQTable_VAL(table, i, u16)); 593 | break; 594 | case AMQP_FIELD_KIND_U32: 595 | value = PyLong_FromUnsignedLong( 596 | AMQTable_VAL(table, i, u32)); 597 | break; 598 | case AMQP_FIELD_KIND_U64: 599 | value = PyLong_FromUnsignedLong( 600 | AMQTable_VAL(table, i, u64)); 601 | break; 602 | case AMQP_FIELD_KIND_F32: 603 | value = PyFloat_FromDouble(AMQTable_VAL(table, i, f32)); 604 | break; 605 | case AMQP_FIELD_KIND_F64: 606 | value = PyFloat_FromDouble(AMQTable_VAL(table, i, f64)); 607 | break; 608 | case AMQP_FIELD_KIND_UTF8: 609 | value = PySTRING_FROM_AMQBYTES( 610 | AMQTable_VAL(table, i, bytes)); 611 | break; 612 | case AMQP_FIELD_KIND_TABLE: 613 | value = AMQTable_toPyDict(&(AMQTable_VAL(table, i, table))); 614 | break; 615 | case AMQP_FIELD_KIND_ARRAY: 616 | value = AMQArray_toPyList(&(AMQTable_VAL(table, i, array))); 617 | break; 618 | } 619 | 620 | key = AMQTable_TO_PYKEY(table, i); 621 | if (value == Py_None || value == NULL) 622 | /* unsupported type */ 623 | PyDICT_SETNONE_DECREF(dict, key); 624 | else 625 | PyDICT_SETKV_DECREF(dict, key, value); 626 | 627 | } 628 | } 629 | return dict; 630 | } 631 | 632 | 633 | static PyObject* 634 | AMQArray_toPyList(amqp_array_t *array) 635 | { 636 | register PyObject *value = NULL; 637 | PyObject *list = NULL; 638 | list = PyList_New((Py_ssize_t) array->num_entries); 639 | 640 | if (array) { 641 | int i; 642 | for (i = 0; i < array->num_entries; ++i, value=NULL) { 643 | switch (array->entries[i].kind) { 644 | case AMQP_FIELD_KIND_BOOLEAN: 645 | value = PyBool_FromLong(AMQArray_VAL(array, i, boolean)); 646 | break; 647 | case AMQP_FIELD_KIND_I8: 648 | value = PyInt_FromLong(AMQArray_VAL(array, i, i8)); 649 | break; 650 | case AMQP_FIELD_KIND_I16: 651 | value = PyInt_FromLong(AMQArray_VAL(array, i, i16)); 652 | break; 653 | case AMQP_FIELD_KIND_I32: 654 | value = PyInt_FromLong(AMQArray_VAL(array, i, i32)); 655 | break; 656 | case AMQP_FIELD_KIND_I64: 657 | value = PyLong_FromLong(AMQArray_VAL(array, i, i64)); 658 | break; 659 | case AMQP_FIELD_KIND_U8: 660 | value = PyLong_FromUnsignedLong(AMQArray_VAL(array, i, u8)); 661 | break; 662 | case AMQP_FIELD_KIND_U16: 663 | value = PyLong_FromUnsignedLong( 664 | AMQArray_VAL(array, i, u16)); 665 | break; 666 | case AMQP_FIELD_KIND_U32: 667 | value = PyLong_FromUnsignedLong( 668 | AMQArray_VAL(array, i, u32)); 669 | break; 670 | case AMQP_FIELD_KIND_U64: 671 | value = PyLong_FromUnsignedLong( 672 | AMQArray_VAL(array, i, u64)); 673 | break; 674 | case AMQP_FIELD_KIND_F32: 675 | value = PyFloat_FromDouble(AMQArray_VAL(array, i, f32)); 676 | break; 677 | case AMQP_FIELD_KIND_F64: 678 | value = PyFloat_FromDouble(AMQArray_VAL(array, i, f64)); 679 | break; 680 | case AMQP_FIELD_KIND_UTF8: 681 | value = PySTRING_FROM_AMQBYTES( 682 | AMQArray_VAL(array, i, bytes)); 683 | break; 684 | case AMQP_FIELD_KIND_TABLE: 685 | value = AMQTable_toPyDict(&(AMQArray_VAL(array, i, table))); 686 | break; 687 | case AMQP_FIELD_KIND_ARRAY: 688 | value = AMQArray_toPyList(&(AMQArray_VAL(array, i, array))); 689 | break; 690 | default: 691 | /* unsupported type */ 692 | Py_INCREF(Py_None); 693 | value = Py_None; 694 | break; 695 | } 696 | 697 | PyList_SET_ITEM(list, i, value); 698 | 699 | } 700 | } 701 | return list; 702 | } 703 | 704 | static int PyDict_to_basic_properties(PyObject *p, 705 | amqp_basic_properties_t *props, 706 | amqp_connection_state_t conn, 707 | amqp_pool_t *pool, 708 | pyobject_array_t *pyobj_array) 709 | { 710 | PyObject *value = NULL; 711 | props->headers = amqp_empty_table; 712 | props->_flags = AMQP_BASIC_HEADERS_FLAG; 713 | 714 | if ((value = PyDict_GetItemString(p, "content_type")) != NULL) { 715 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 716 | props->content_type = PyString_AS_AMQBYTES(value); 717 | props->_flags |= AMQP_BASIC_CONTENT_TYPE_FLAG; 718 | } 719 | if ((value = PyDict_GetItemString(p, "content_encoding")) != NULL) { 720 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 721 | props->content_encoding = PyString_AS_AMQBYTES(value); 722 | props->_flags |= AMQP_BASIC_CONTENT_ENCODING_FLAG; 723 | } 724 | if ((value = PyDict_GetItemString(p, "correlation_id")) != NULL) { 725 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 726 | props->correlation_id = PyString_AS_AMQBYTES(value); 727 | props->_flags |= AMQP_BASIC_CORRELATION_ID_FLAG; 728 | } 729 | if ((value = PyDict_GetItemString(p, "reply_to")) != NULL) { 730 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 731 | props->reply_to = PyString_AS_AMQBYTES(value); 732 | props->_flags |= AMQP_BASIC_REPLY_TO_FLAG; 733 | } 734 | if ((value = PyDict_GetItemString(p, "expiration")) != NULL) { 735 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 736 | props->expiration = PyString_AS_AMQBYTES(value); 737 | props->_flags |= AMQP_BASIC_EXPIRATION_FLAG; 738 | } 739 | if ((value = PyDict_GetItemString(p, "message_id")) != NULL) { 740 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 741 | props->message_id = PyString_AS_AMQBYTES(value); 742 | props->_flags |= AMQP_BASIC_MESSAGE_ID_FLAG; 743 | } 744 | if ((value = PyDict_GetItemString(p, "type")) != NULL) { 745 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 746 | props->type = PyString_AS_AMQBYTES(value); 747 | props->_flags |= AMQP_BASIC_TYPE_FLAG; 748 | } 749 | if ((value = PyDict_GetItemString(p, "user_id")) != NULL) { 750 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 751 | props->user_id = PyString_AS_AMQBYTES(value); 752 | props->_flags |= AMQP_BASIC_USER_ID_FLAG; 753 | } 754 | if ((value = PyDict_GetItemString(p, "app_id")) != NULL) { 755 | if ((value = PyObjectArray_Maybe_Unicode(value, pyobj_array)) == NULL) return -1; 756 | props->app_id = PyString_AS_AMQBYTES(value); 757 | props->_flags |= AMQP_BASIC_APP_ID_FLAG; 758 | } 759 | if ((value = PyDict_GetItemString(p, "delivery_mode")) != NULL) { 760 | props->delivery_mode = (uint8_t)PyInt_AS_LONG(value); 761 | props->_flags |= AMQP_BASIC_DELIVERY_MODE_FLAG; 762 | } 763 | if ((value = PyDict_GetItemString(p, "priority")) != NULL) { 764 | props->priority = (uint8_t)PyInt_AS_LONG(value); 765 | props->_flags |= AMQP_BASIC_PRIORITY_FLAG; 766 | } 767 | if ((value = PyDict_GetItemString(p, "timestamp")) != NULL) { 768 | props->timestamp = (uint64_t)PyInt_AS_LONG(value); 769 | props->_flags |= AMQP_BASIC_TIMESTAMP_FLAG; 770 | } 771 | 772 | if ((value = PyDict_GetItemString(p, "headers")) != NULL) { 773 | props->headers = PyDict_ToAMQTable(conn, value, pool, pyobj_array); 774 | if (PyErr_Occurred()) return -1; 775 | } 776 | return 1; 777 | } 778 | 779 | _PYRMQ_INLINE void 780 | amqp_basic_deliver_to_PyDict(PyObject *dest, 781 | uint64_t delivery_tag, 782 | amqp_bytes_t exchange, 783 | amqp_bytes_t routing_key, 784 | amqp_boolean_t redelivered) 785 | { 786 | PyObject *value = NULL; 787 | 788 | /* -- delivery_tag (PyInt) */ 789 | value = PyLong_FromLongLong(delivery_tag); 790 | PyDICT_SETSTR_DECREF(dest, "delivery_tag", value); 791 | 792 | /* -- exchange (PyString) */ 793 | value = PySTRING_FROM_AMQBYTES(exchange); 794 | PyDICT_SETSTR_DECREF(dest, "exchange", value); 795 | 796 | /* -- routing_key (PyString) */ 797 | value = PySTRING_FROM_AMQBYTES(routing_key); 798 | PyDICT_SETSTR_DECREF(dest, "routing_key", value); 799 | 800 | /* -- redelivered (PyBool) */ 801 | value = PyBool_FromLong((long)redelivered); 802 | PyDICT_SETSTR_DECREF(dest, "redelivered", value); 803 | 804 | return; 805 | } 806 | 807 | /* ------: Keep track of increased reference counts :------------------------ */ 808 | 809 | _PYRMQ_INLINE PyObject* 810 | PyObjectArray_Maybe_Unicode(PyObject *s, pyobject_array_t *array) 811 | { 812 | if (PyUnicode_Check(s)) { 813 | return PyObjectArray_AddEntry(array, PyUnicode_AsASCIIString(s)); 814 | } 815 | return s; 816 | } 817 | 818 | _PYRMQ_INLINE PyObject* 819 | PyObjectArray_AddEntry(pyobject_array_t *array, PyObject *obj) 820 | { 821 | if (!obj) { 822 | return obj; 823 | } 824 | 825 | if (array->num_entries == PYOBJECT_ARRAY_MAX) { 826 | if (!array->next) { 827 | array->next = (pyobject_array_t *) calloc(1, sizeof(pyobject_array_t)); 828 | } 829 | 830 | return PyObjectArray_AddEntry(array->next, obj); 831 | } 832 | 833 | array->entries[array->num_entries] = obj; 834 | array->num_entries++; 835 | 836 | return obj; 837 | } 838 | 839 | static void PyObjectArray_XDECREF(pyobject_array_t *array) 840 | { 841 | int i; 842 | 843 | if (!array) { 844 | return; 845 | } 846 | 847 | if (array->next) { 848 | PyObjectArray_XDECREF(array->next); 849 | free(array->next); 850 | array->next = (pyobject_array_t *) 0; 851 | } 852 | 853 | 854 | for (i = 0; i < array->num_entries; ++i) { 855 | Py_XDECREF(array->entries[i]); 856 | } 857 | 858 | array->num_entries = 0; 859 | } 860 | 861 | /* ------: Error Handlers :----------------------------------------------- */ 862 | 863 | int PyRabbitMQ_HandleError(int ret, char const *context) 864 | { 865 | if (ret < 0) { 866 | char errorstr[1024]; 867 | snprintf(errorstr, sizeof(errorstr), "%s: %s", 868 | context, strerror(-ret)); 869 | PyErr_SetString(PyRabbitMQExc_ConnectionError, errorstr); 870 | return 0; 871 | } 872 | return 1; 873 | } 874 | 875 | 876 | _PYRMQ_INLINE int 877 | PyRabbitMQ_HandlePollError(int ready) 878 | { 879 | if (ready < 0 && !PyErr_Occurred()) 880 | PyErr_SetFromErrno(PyExc_OSError); 881 | if (!ready && !PyErr_Occurred()) 882 | PyErr_SetString(PyRabbitMQ_socket_timeout, "timed out"); 883 | return ready; 884 | } 885 | 886 | 887 | int PyRabbitMQ_HandleAMQStatus(int status, const char *context) 888 | { 889 | char errorstr[1024]; 890 | if (status) { 891 | snprintf(errorstr, sizeof(errorstr), "%s: %s", 892 | context, amqp_error_string2(status)); 893 | PyErr_SetString(PyRabbitMQExc_ConnectionError, errorstr); 894 | } 895 | return status; 896 | } 897 | 898 | 899 | int PyRabbitMQ_HandleAMQError(PyRabbitMQ_Connection *self, unsigned int channel, 900 | amqp_rpc_reply_t reply, const char *context) 901 | { 902 | char errorstr[1024]; 903 | 904 | switch (reply.reply_type) { 905 | case AMQP_RESPONSE_NORMAL: 906 | return 0; 907 | 908 | case AMQP_RESPONSE_NONE: 909 | snprintf(errorstr, sizeof(errorstr), 910 | "%s: missing RPC reply type!", context); 911 | goto connerror; 912 | 913 | case AMQP_RESPONSE_LIBRARY_EXCEPTION: 914 | snprintf(errorstr, sizeof(errorstr), "%s: %s", 915 | context, 916 | reply.library_error 917 | ? amqp_error_string2(reply.library_error) 918 | : "(end-of-stream)"); 919 | goto connerror; 920 | 921 | case AMQP_RESPONSE_SERVER_EXCEPTION: 922 | 923 | switch (reply.reply.id) { 924 | case AMQP_CONNECTION_CLOSE_METHOD: { 925 | amqp_connection_close_t *m = (amqp_connection_close_t *) reply.reply.decoded; 926 | snprintf(errorstr, sizeof(errorstr), 927 | "%s: server connection error %d, message: %.*s", 928 | context, 929 | m->reply_code, 930 | (int) m->reply_text.len, (char *) m->reply_text.bytes); 931 | goto connerror; 932 | } 933 | case AMQP_CHANNEL_CLOSE_METHOD: { 934 | amqp_channel_close_t *m = (amqp_channel_close_t *) reply.reply.decoded; 935 | snprintf(errorstr, sizeof(errorstr), 936 | "%s: server channel error %d, message: %.*s", 937 | context, m->reply_code, 938 | (int) m->reply_text.len, (char *) m->reply_text.bytes); 939 | goto chanerror; 940 | } 941 | default: 942 | snprintf(errorstr, sizeof(errorstr), 943 | "%s: unknown server error, method id 0x%08X", 944 | context, reply.reply.id); 945 | goto connerror; 946 | } 947 | break; 948 | } 949 | connerror: 950 | PyErr_SetString(PyRabbitMQExc_ConnectionError, errorstr); 951 | PyRabbitMQ_Connection_close(self); 952 | return PYRABBITMQ_CONNECTION_ERROR; 953 | chanerror: 954 | PyErr_SetString(PyRabbitMQExc_ChannelError, errorstr); 955 | PyRabbitMQ_revive_channel(self, channel); 956 | return PYRABBITMQ_CHANNEL_ERROR; 957 | } 958 | 959 | 960 | void PyRabbitMQ_SetErr_UnexpectedHeader(amqp_frame_t* frame) 961 | { 962 | char errorstr[1024]; 963 | snprintf(errorstr, sizeof(errorstr), 964 | "Unexpected header %d", frame->frame_type); 965 | PyErr_SetString(PyRabbitMQExc_ChannelError, errorstr); 966 | } 967 | 968 | unsigned int 969 | PyRabbitMQ_revive_channel(PyRabbitMQ_Connection *self, unsigned int channel) 970 | { 971 | int status = -1; 972 | amqp_channel_close_ok_t req; 973 | status = amqp_send_method(self->conn, (amqp_channel_t)channel, 974 | AMQP_CHANNEL_CLOSE_OK_METHOD, &req); 975 | if (status < 0) { 976 | PyErr_SetString(PyRabbitMQExc_ConnectionError, "Couldn't revive channel"); 977 | PyRabbitMQ_Connection_close(self); 978 | return 1; 979 | } 980 | return PyRabbitMQ_Connection_create_channel(self, channel); 981 | } 982 | 983 | /* ------: Connection :--------------------------------------------------- */ 984 | 985 | /* 986 | * Connection.__new__() 987 | * */ 988 | static PyRabbitMQ_Connection* 989 | PyRabbitMQ_ConnectionType_new(PyTypeObject *type, 990 | PyObject *args, PyObject *kwargs) 991 | { 992 | PyRabbitMQ_Connection *self; 993 | 994 | self = (PyRabbitMQ_Connection *)PyType_GenericNew(type, args, kwargs); 995 | if (self != NULL) { 996 | self->conn = NULL; 997 | self->hostname = NULL; 998 | self->userid = NULL; 999 | self->password = NULL; 1000 | self->virtual_host = NULL; 1001 | self->port = 5672; 1002 | self->sockfd = 0; 1003 | self->connected = 0; 1004 | self->server_properties = NULL; 1005 | self->callbacks = NULL; 1006 | } 1007 | return self; 1008 | } 1009 | 1010 | 1011 | /* 1012 | * Connection.__del__() 1013 | */ 1014 | static void 1015 | PyRabbitMQ_ConnectionType_dealloc(PyRabbitMQ_Connection *self) 1016 | { 1017 | if (self->weakreflist != NULL) 1018 | PyObject_ClearWeakRefs((PyObject*)self); 1019 | 1020 | if (self->hostname != NULL) 1021 | PyMem_Free(self->hostname); 1022 | 1023 | if (self->userid != NULL) 1024 | PyMem_Free(self->userid); 1025 | 1026 | if (self->password != NULL) 1027 | PyMem_Free(self->password); 1028 | 1029 | if (self->virtual_host != NULL) 1030 | PyMem_Free(self->virtual_host); 1031 | 1032 | Py_XDECREF(self->callbacks); 1033 | Py_XDECREF(self->client_properties); 1034 | Py_XDECREF(self->server_properties); 1035 | Py_TYPE(self)->tp_free(self); 1036 | } 1037 | 1038 | 1039 | /* 1040 | * Connection.__init__() 1041 | */ 1042 | static int 1043 | PyRabbitMQ_ConnectionType_init(PyRabbitMQ_Connection *self, 1044 | PyObject *args, PyObject *kwargs) 1045 | { 1046 | static char *kwlist[] = { 1047 | "hostname", 1048 | "userid", 1049 | "password", 1050 | "virtual_host", 1051 | "port", 1052 | "channel_max", 1053 | "frame_max", 1054 | "heartbeat", 1055 | "client_properties", 1056 | "connect_timeout", 1057 | NULL 1058 | }; 1059 | char *hostname; 1060 | char *userid; 1061 | char *password; 1062 | char *virtual_host; 1063 | 1064 | int channel_max = 0xffff; 1065 | int frame_max = 131072; 1066 | int heartbeat = 0; 1067 | int port = 5672; 1068 | int connect_timeout = 0; 1069 | PyObject *client_properties = NULL; 1070 | 1071 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ssssiiiiOi", kwlist, 1072 | &hostname, &userid, &password, &virtual_host, &port, 1073 | &channel_max, &frame_max, &heartbeat, &client_properties, 1074 | &connect_timeout)) { 1075 | return -1; 1076 | } 1077 | 1078 | self->hostname = PyMem_Malloc(strlen(hostname) + 1); 1079 | self->userid = PyMem_Malloc(strlen(userid) + 1); 1080 | self->password = PyMem_Malloc(strlen(password) + 1); 1081 | self->virtual_host = PyMem_Malloc(strlen(virtual_host) + 1); 1082 | 1083 | if (self->hostname == NULL || self->userid == NULL || self->password == NULL || self->virtual_host == NULL) { 1084 | PyErr_NoMemory(); 1085 | return 0; 1086 | } 1087 | 1088 | strcpy(self->hostname, hostname); 1089 | strcpy(self->userid, userid); 1090 | strcpy(self->password, password); 1091 | strcpy(self->virtual_host, virtual_host); 1092 | 1093 | self->port = port; 1094 | self->channel_max = channel_max; 1095 | self->frame_max = frame_max; 1096 | self->heartbeat = heartbeat; 1097 | self->connect_timeout = connect_timeout; 1098 | self->weakreflist = NULL; 1099 | self->callbacks = PyDict_New(); 1100 | if (self->callbacks == NULL) return -1; 1101 | 1102 | Py_XINCREF(client_properties); 1103 | self->client_properties = client_properties; 1104 | self->server_properties = NULL; 1105 | return 0; 1106 | } 1107 | 1108 | 1109 | /* 1110 | * Connection.fileno() 1111 | */ 1112 | static PyObject* 1113 | PyRabbitMQ_Connection_fileno(PyRabbitMQ_Connection *self) 1114 | { 1115 | if (self->sockfd > 0) { 1116 | return PyInt_FromLong((long)self->sockfd); 1117 | } 1118 | else { 1119 | PyErr_SetString(PyExc_ValueError, "Socket not connected"); 1120 | return 0; 1121 | } 1122 | } 1123 | 1124 | 1125 | /* 1126 | * Connection.connect() 1127 | */ 1128 | static PyObject* 1129 | PyRabbitMQ_Connection_connect(PyRabbitMQ_Connection *self) 1130 | { 1131 | int status; 1132 | amqp_socket_t *socket = NULL; 1133 | amqp_rpc_reply_t reply; 1134 | amqp_pool_t pool; 1135 | amqp_table_t properties; 1136 | struct timeval timeout = {0, 0}; 1137 | 1138 | pyobject_array_t pyobj_array = {0}; 1139 | 1140 | if (self->connected) { 1141 | PyErr_SetString(PyRabbitMQExc_ConnectionError, "Already connected"); 1142 | goto bail; 1143 | } 1144 | Py_BEGIN_ALLOW_THREADS; 1145 | self->conn = amqp_new_connection(); 1146 | socket = amqp_tcp_socket_new(self->conn); 1147 | Py_END_ALLOW_THREADS; 1148 | 1149 | if (!socket) { 1150 | PyErr_NoMemory(); 1151 | goto error; 1152 | } 1153 | Py_BEGIN_ALLOW_THREADS; 1154 | if (self->connect_timeout <= 0) { 1155 | status = amqp_socket_open(socket, self->hostname, self->port); 1156 | } else { 1157 | timeout.tv_sec = self->connect_timeout; 1158 | status = amqp_socket_open_noblock(socket, self->hostname, self->port, &timeout); 1159 | } 1160 | 1161 | Py_END_ALLOW_THREADS; 1162 | if (PyRabbitMQ_HandleAMQStatus(status, "Error opening socket")) { 1163 | goto error; 1164 | } 1165 | 1166 | Py_BEGIN_ALLOW_THREADS; 1167 | self->sockfd = amqp_socket_get_sockfd(socket); 1168 | 1169 | if (self->client_properties != NULL && PyDict_Check(self->client_properties)) { 1170 | init_amqp_pool(&pool, self->frame_max); 1171 | properties = PyDict_ToAMQTable(self->conn, self->client_properties, &pool, &pyobj_array); 1172 | 1173 | reply = amqp_login_with_properties(self->conn, self->virtual_host, self->channel_max, 1174 | self->frame_max, self->heartbeat, 1175 | &properties, 1176 | AMQP_SASL_METHOD_PLAIN, self->userid, self->password); 1177 | PyObjectArray_XDECREF(&pyobj_array); 1178 | } else { 1179 | reply = amqp_login(self->conn, self->virtual_host, self->channel_max, 1180 | self->frame_max, self->heartbeat, 1181 | AMQP_SASL_METHOD_PLAIN, self->userid, self->password); 1182 | } 1183 | 1184 | Py_END_ALLOW_THREADS; 1185 | 1186 | if (PyRabbitMQ_HandleAMQError(self, 0, reply, "Couldn't log in")) 1187 | goto bail; 1188 | 1189 | /* after tune */ 1190 | self->connected = 1; 1191 | self->channel_max = self->conn->channel_max; 1192 | self->frame_max = self->conn->frame_max; 1193 | self->heartbeat = self->conn->heartbeat; 1194 | self->server_properties = AMQTable_toPyDict(amqp_get_server_properties(self->conn)); 1195 | Py_RETURN_NONE; 1196 | error: 1197 | PyRabbitMQ_Connection_close(self); 1198 | bail: 1199 | PyObjectArray_XDECREF(&pyobj_array); 1200 | 1201 | return 0; 1202 | } 1203 | 1204 | 1205 | /* 1206 | * Connection._close() 1207 | */ 1208 | static PyObject* 1209 | PyRabbitMQ_Connection_close(PyRabbitMQ_Connection *self) 1210 | { 1211 | if (self->connected) { 1212 | self->connected = 0; 1213 | 1214 | Py_BEGIN_ALLOW_THREADS 1215 | amqp_connection_close(self->conn, AMQP_REPLY_SUCCESS); 1216 | amqp_destroy_connection(self->conn); 1217 | self->sockfd = 0; 1218 | Py_END_ALLOW_THREADS 1219 | } 1220 | 1221 | Py_RETURN_NONE; 1222 | } 1223 | 1224 | 1225 | unsigned int 1226 | PyRabbitMQ_Connection_create_channel(PyRabbitMQ_Connection *self, unsigned int channel) 1227 | { 1228 | amqp_rpc_reply_t reply; 1229 | 1230 | Py_BEGIN_ALLOW_THREADS; 1231 | amqp_channel_open(self->conn, channel); 1232 | reply = amqp_get_rpc_reply(self->conn); 1233 | Py_END_ALLOW_THREADS; 1234 | 1235 | return PyRabbitMQ_HandleAMQError(self, 0, reply, "Couldn't create channel"); 1236 | } 1237 | 1238 | 1239 | /* 1240 | * Connection._channel_open 1241 | */ 1242 | static PyObject * 1243 | PyRabbitMQ_Connection_channel_open(PyRabbitMQ_Connection *self, PyObject *args) 1244 | { 1245 | unsigned int channel; 1246 | 1247 | if (PyRabbitMQ_Not_Connected(self)) 1248 | goto bail; 1249 | 1250 | if (!PyArg_ParseTuple(args, "I", &channel)) 1251 | goto bail; 1252 | 1253 | if (PyRabbitMQ_Connection_create_channel(self, channel)) 1254 | goto bail;; 1255 | 1256 | Py_RETURN_NONE; 1257 | bail: 1258 | return 0; 1259 | } 1260 | 1261 | 1262 | /* 1263 | * Connection._channel_close 1264 | */ 1265 | 1266 | unsigned int 1267 | PyRabbitMQ_Connection_destroy_channel(PyRabbitMQ_Connection *self, 1268 | unsigned int channel) 1269 | { 1270 | amqp_rpc_reply_t reply; 1271 | Py_BEGIN_ALLOW_THREADS; 1272 | reply = amqp_channel_close(self->conn, channel, AMQP_REPLY_SUCCESS); 1273 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 1274 | Py_END_ALLOW_THREADS; 1275 | 1276 | return PyRabbitMQ_HandleAMQError(self, channel, reply, "Couldn't close channel"); 1277 | } 1278 | 1279 | static PyObject* 1280 | PyRabbitMQ_Connection_channel_close(PyRabbitMQ_Connection *self, 1281 | PyObject *args) 1282 | { 1283 | unsigned int channel = 0; 1284 | 1285 | if (PyRabbitMQ_Not_Connected(self)) 1286 | goto error; 1287 | 1288 | if (!PyArg_ParseTuple(args, "I", &channel)) 1289 | goto error; 1290 | 1291 | if (PyRabbitMQ_Connection_destroy_channel(self, channel)) 1292 | goto error; 1293 | 1294 | Py_RETURN_NONE; 1295 | 1296 | error: 1297 | return 0; 1298 | } 1299 | 1300 | _PYRMQ_INLINE int 1301 | PyRabbitMQ_ApplyCallback(PyRabbitMQ_Connection *self, 1302 | PyObject *consumer_tag, 1303 | PyObject *channel, 1304 | PyObject *propdict, 1305 | PyObject *delivery_info, 1306 | PyObject *view) 1307 | { 1308 | int retval = 0; 1309 | PyObject *channel_callbacks = NULL; 1310 | PyObject *callback_for_tag = NULL; 1311 | PyObject *channels = NULL; 1312 | PyObject *channelobj = NULL; 1313 | PyObject *Message = NULL; 1314 | PyObject *message = NULL; 1315 | PyObject *args = NULL; 1316 | PyObject *callback_result = NULL; 1317 | 1318 | /* self.callbacks */ 1319 | if (!(channel_callbacks = PyDict_GetItem(self->callbacks, channel))) 1320 | return -1; 1321 | 1322 | /* self.callbacks[consumer_tag] */ 1323 | if (!(callback_for_tag = PyDict_GetItem(channel_callbacks, consumer_tag))) 1324 | goto error; 1325 | 1326 | /* self.channels */ 1327 | if (!(channels = PyObject_GetAttrString((PyObject *)self, "channels"))) 1328 | goto error; 1329 | 1330 | /* self.channels[channel] */ 1331 | if (!(channelobj = PyDict_GetItem(channels, channel))) 1332 | goto error; 1333 | 1334 | /* message = self.Message(channel, properties, delivery_info, body) */ 1335 | Message = BUILD_METHOD_NAME("Message"); 1336 | message = PyObject_CallMethodObjArgs((PyObject *)self, Message, 1337 | channelobj, propdict, delivery_info, view, NULL); 1338 | if (!message) 1339 | goto error; 1340 | 1341 | /* callback(message) */ 1342 | if ((args = PyTuple_New(1)) == NULL) { 1343 | Py_DECREF(message); 1344 | goto finally; 1345 | } 1346 | PyTuple_SET_ITEM(args, 0, message); 1347 | 1348 | callback_result = PyObject_CallObject(callback_for_tag, args); 1349 | Py_XDECREF(callback_result); 1350 | 1351 | goto finally; 1352 | error: 1353 | retval = -1; 1354 | finally: 1355 | Py_XDECREF(args); 1356 | Py_XDECREF(channels); 1357 | Py_XDECREF(Message); 1358 | return retval; 1359 | } 1360 | 1361 | void 1362 | PyRabbitMQ_SetErr_ReceivedFrame(PyRabbitMQ_Connection *self, amqp_frame_t* frame) 1363 | { 1364 | static char errstr[512]; 1365 | 1366 | switch(frame->payload.method.id) { 1367 | case AMQP_CHANNEL_CLOSE_METHOD: { 1368 | amqp_channel_close_t *chanm = (amqp_channel_close_t *)frame->payload.method.decoded; 1369 | snprintf(errstr, sizeof(errstr), 1370 | "channel error %d, message: %.*s", 1371 | chanm->reply_code, 1372 | (int) chanm->reply_text.len, 1373 | (char *) chanm->reply_text.bytes); 1374 | PyErr_SetString(PyRabbitMQExc_ChannelError, errstr); 1375 | PyRabbitMQ_revive_channel(self, frame->channel); 1376 | break; 1377 | } 1378 | case AMQP_CONNECTION_CLOSE_METHOD: { 1379 | amqp_connection_close_t *connm = (amqp_connection_close_t *)frame->payload.method.decoded; 1380 | snprintf(errstr, sizeof(errstr), 1381 | "server connection error %d message: %.*s", 1382 | connm->reply_code, 1383 | (int) connm->reply_text.len, 1384 | (char *) connm->reply_text.bytes); 1385 | PyErr_SetString(PyRabbitMQExc_ConnectionError, errstr); 1386 | PyRabbitMQ_Connection_close(self); 1387 | break; 1388 | } 1389 | default: { 1390 | PyErr_SetString(PyRabbitMQExc_ConnectionError, "Bad frame read"); 1391 | break; 1392 | } 1393 | } 1394 | } 1395 | 1396 | int 1397 | PyRabbitMQ_recv(PyRabbitMQ_Connection *self, PyObject *p, 1398 | amqp_connection_state_t conn, int piggyback) 1399 | { 1400 | amqp_frame_t frame; 1401 | amqp_channel_t cur_channel = 0; 1402 | amqp_basic_deliver_t *deliver; 1403 | amqp_basic_properties_t *props; 1404 | Py_ssize_t body_target; 1405 | Py_ssize_t body_received; 1406 | PyObject *channel = NULL; 1407 | PyObject *consumer_tag = NULL; 1408 | PyObject *delivery_info = NULL; 1409 | PyObject *propdict = PyDict_New(); 1410 | PyObject *payload = NULL; 1411 | PyObject *view = NULL; 1412 | char *buf = NULL; 1413 | char *bufp = NULL; 1414 | unsigned int i = 0; 1415 | register unsigned int j = 0; 1416 | int retval = 0; 1417 | 1418 | memset(&props, 0, sizeof(props)); 1419 | 1420 | while (1) { 1421 | if (!piggyback) { 1422 | Py_BEGIN_ALLOW_THREADS; 1423 | amqp_maybe_release_buffers(conn); 1424 | retval = amqp_simple_wait_frame(conn, &frame); 1425 | Py_END_ALLOW_THREADS; 1426 | if (retval < 0) break; 1427 | if (frame.frame_type != AMQP_FRAME_METHOD) continue; 1428 | if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) goto altframe; 1429 | 1430 | cur_channel = frame.channel; 1431 | 1432 | delivery_info = PyDict_New(); 1433 | deliver = (amqp_basic_deliver_t *)frame.payload.method.decoded; 1434 | /* need consumer tag for later. 1435 | * add delivery info to the delivery_info dict. */ 1436 | amqp_basic_deliver_to_PyDict(delivery_info, 1437 | deliver->delivery_tag, 1438 | deliver->exchange, 1439 | deliver->routing_key, 1440 | deliver->redelivered); 1441 | /* add in the consumer_tag */ 1442 | consumer_tag = PySTRING_FROM_AMQBYTES(deliver->consumer_tag); 1443 | PyDict_SetItemString(delivery_info, "consumer_tag", consumer_tag); 1444 | Py_XDECREF(consumer_tag); 1445 | 1446 | piggyback = 0; 1447 | } 1448 | 1449 | Py_BEGIN_ALLOW_THREADS; 1450 | retval = cur_channel == 0 ? 1451 | amqp_simple_wait_frame(conn, &frame) : 1452 | amqp_simple_wait_frame_on_channel(conn, cur_channel, &frame); 1453 | Py_END_ALLOW_THREADS; 1454 | if (retval < 0) break; 1455 | 1456 | if (frame.frame_type == AMQP_FRAME_METHOD) goto altframe; 1457 | if (frame.frame_type != AMQP_FRAME_HEADER) { 1458 | PyRabbitMQ_SetErr_UnexpectedHeader(&frame); 1459 | goto finally; 1460 | } 1461 | 1462 | /* if piggybacked, 'channel' is still 0 at this point */ 1463 | cur_channel = frame.channel; 1464 | 1465 | /* channel */ 1466 | channel = PyInt_FromLong((unsigned long)frame.channel); 1467 | 1468 | /* properties */ 1469 | props = (amqp_basic_properties_t *)frame.payload.properties.decoded; 1470 | basic_properties_to_PyDict(props, propdict); 1471 | 1472 | /* body */ 1473 | body_target = frame.payload.properties.body_size; 1474 | body_received = 0; 1475 | 1476 | for (i = 0; body_received < body_target; i++) { 1477 | Py_BEGIN_ALLOW_THREADS; 1478 | retval = amqp_simple_wait_frame_on_channel(conn, cur_channel, &frame); 1479 | Py_END_ALLOW_THREADS; 1480 | if (retval < 0) break; 1481 | 1482 | if (frame.frame_type == AMQP_FRAME_METHOD) goto altframe; 1483 | if (frame.frame_type != AMQP_FRAME_BODY) { 1484 | PyErr_SetString(PyRabbitMQExc_ChannelError, 1485 | "Expected body, got unexpected frame"); 1486 | goto finally; 1487 | } 1488 | bufp = frame.payload.body_fragment.bytes; 1489 | body_received += frame.payload.body_fragment.len; 1490 | if (!i) { 1491 | if (body_received < body_target) { 1492 | payload = PyBytes_FromStringAndSize(NULL, 1493 | (Py_ssize_t)body_target); 1494 | if (!payload) 1495 | goto finally; 1496 | buf = PyBytes_AsString(payload); 1497 | if (!buf) 1498 | goto finally; 1499 | view = PyMemoryView_FromObject(payload); 1500 | } 1501 | else { 1502 | if (p) { 1503 | payload = PySTRING_FROM_AMQBYTES( 1504 | frame.payload.body_fragment); 1505 | } else { 1506 | view = buffer_toMemoryView(bufp, (Py_ssize_t)frame.payload.body_fragment.len); 1507 | } 1508 | break; 1509 | } 1510 | } 1511 | for (j = 0; 1512 | j < frame.payload.body_fragment.len; 1513 | *buf++ = *bufp++, j++); 1514 | } 1515 | if (p) { 1516 | if (!payload) { 1517 | /* expected content, got none */ 1518 | if (body_target) goto error; 1519 | 1520 | /* did not expect content, return empty string */ 1521 | payload = PyBytes_FromStringAndSize(NULL, 0); 1522 | } 1523 | 1524 | PyDict_SetItemString(p, "properties", propdict); 1525 | PyDict_SetItemString(p, "body", payload); 1526 | PyDict_SetItemString(p, "channel", channel); 1527 | retval = 0; 1528 | } else { 1529 | if (!view) goto error; 1530 | retval = PyRabbitMQ_ApplyCallback(self, 1531 | consumer_tag, channel, propdict, delivery_info, view); 1532 | } 1533 | break; 1534 | } 1535 | goto finally; 1536 | 1537 | altframe: 1538 | PyRabbitMQ_SetErr_ReceivedFrame(self, &frame); 1539 | error: 1540 | retval = -1; 1541 | 1542 | finally: 1543 | Py_XDECREF(payload); 1544 | Py_XDECREF(channel); 1545 | Py_XDECREF(propdict); 1546 | Py_XDECREF(delivery_info); 1547 | Py_XDECREF(view); 1548 | return retval; 1549 | } 1550 | 1551 | 1552 | /* 1553 | * Connection._queue_bind 1554 | */ 1555 | static PyObject* 1556 | PyRabbitMQ_Connection_queue_bind(PyRabbitMQ_Connection *self, 1557 | PyObject *args) 1558 | { 1559 | unsigned int channel; 1560 | PyObject *queue = NULL; 1561 | PyObject *exchange = NULL; 1562 | PyObject *routing_key = NULL; 1563 | PyObject *arguments = NULL; 1564 | 1565 | amqp_table_t bargs = amqp_empty_table; 1566 | amqp_pool_t *channel_pool = NULL; 1567 | amqp_rpc_reply_t reply; 1568 | 1569 | pyobject_array_t pyobj_array = {0}; 1570 | 1571 | if (PyRabbitMQ_Not_Connected(self)) 1572 | goto bail; 1573 | 1574 | if (!PyArg_ParseTuple(args, "IOOOO", 1575 | &channel, &queue, &exchange, &routing_key, &arguments)) 1576 | goto bail; 1577 | if ((queue = PyObjectArray_Maybe_Unicode(queue, &pyobj_array)) == NULL) goto bail; 1578 | if ((exchange = PyObjectArray_Maybe_Unicode(exchange, &pyobj_array)) == NULL) goto bail; 1579 | if ((routing_key = PyObjectArray_Maybe_Unicode(routing_key, &pyobj_array)) == NULL) goto bail; 1580 | 1581 | channel_pool = amqp_get_or_create_channel_pool(self->conn, (amqp_channel_t)channel); 1582 | if (channel_pool == NULL) { 1583 | PyErr_NoMemory(); 1584 | goto bail; 1585 | } 1586 | bargs = PyDict_ToAMQTable(self->conn, arguments, channel_pool, &pyobj_array); 1587 | if (PyErr_Occurred()) 1588 | goto bail; 1589 | 1590 | Py_BEGIN_ALLOW_THREADS; 1591 | amqp_queue_bind(self->conn, channel, 1592 | PyString_AS_AMQBYTES(queue), 1593 | PyString_AS_AMQBYTES(exchange), 1594 | PyString_AS_AMQBYTES(routing_key), 1595 | bargs); 1596 | reply = amqp_get_rpc_reply(self->conn); 1597 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 1598 | Py_END_ALLOW_THREADS; 1599 | PyObjectArray_XDECREF(&pyobj_array); 1600 | 1601 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "queue.bind")) 1602 | goto bail; 1603 | 1604 | Py_RETURN_NONE; 1605 | bail: 1606 | PyObjectArray_XDECREF(&pyobj_array); 1607 | 1608 | return 0; 1609 | } 1610 | 1611 | 1612 | /* 1613 | * Connection._queue_unbind 1614 | */ 1615 | static PyObject* 1616 | PyRabbitMQ_Connection_queue_unbind(PyRabbitMQ_Connection *self, 1617 | PyObject *args) 1618 | { 1619 | unsigned int channel; 1620 | PyObject *queue = NULL; 1621 | PyObject *exchange = NULL; 1622 | PyObject *routing_key = NULL; 1623 | PyObject *arguments = NULL; 1624 | 1625 | amqp_table_t uargs = amqp_empty_table; 1626 | amqp_pool_t *channel_pool = NULL; 1627 | amqp_rpc_reply_t reply; 1628 | 1629 | pyobject_array_t pyobj_array = {0}; 1630 | 1631 | if (PyRabbitMQ_Not_Connected(self)) 1632 | goto bail; 1633 | 1634 | if (!PyArg_ParseTuple(args, "IOOOO", 1635 | &channel, &queue, &exchange, &routing_key, &arguments)) 1636 | goto bail; 1637 | if ((queue = PyObjectArray_Maybe_Unicode(queue, &pyobj_array)) == NULL) goto bail; 1638 | if ((exchange = PyObjectArray_Maybe_Unicode(exchange, &pyobj_array)) == NULL) goto bail; 1639 | if ((routing_key = PyObjectArray_Maybe_Unicode(routing_key, &pyobj_array)) == NULL) goto bail; 1640 | 1641 | channel_pool = amqp_get_or_create_channel_pool(self->conn, (amqp_channel_t)channel); 1642 | if (channel_pool == NULL) { 1643 | PyErr_NoMemory(); 1644 | goto bail; 1645 | } 1646 | uargs = PyDict_ToAMQTable(self->conn, arguments, channel_pool, &pyobj_array); 1647 | if (PyErr_Occurred()) 1648 | goto bail; 1649 | 1650 | Py_BEGIN_ALLOW_THREADS; 1651 | amqp_queue_unbind(self->conn, channel, 1652 | PyString_AS_AMQBYTES(queue), 1653 | PyString_AS_AMQBYTES(exchange), 1654 | PyString_AS_AMQBYTES(routing_key), 1655 | uargs); 1656 | reply = amqp_get_rpc_reply(self->conn); 1657 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 1658 | Py_END_ALLOW_THREADS; 1659 | PyObjectArray_XDECREF(&pyobj_array); 1660 | 1661 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "queue.unbind")) 1662 | goto bail; 1663 | 1664 | Py_RETURN_NONE; 1665 | bail: 1666 | PyObjectArray_XDECREF(&pyobj_array); 1667 | 1668 | return 0; 1669 | } 1670 | 1671 | /* 1672 | * Connection._queue_delete 1673 | */ 1674 | static PyObject* 1675 | PyRabbitMQ_Connection_queue_delete(PyRabbitMQ_Connection *self, 1676 | PyObject *args) 1677 | { 1678 | PyObject *queue = NULL; 1679 | unsigned int channel = 0; 1680 | unsigned int if_unused = 0; 1681 | unsigned int if_empty = 0; 1682 | 1683 | amqp_queue_delete_ok_t *ok; 1684 | amqp_rpc_reply_t reply; 1685 | 1686 | pyobject_array_t pyobj_array = {0}; 1687 | 1688 | if (PyRabbitMQ_Not_Connected(self)) 1689 | goto bail; 1690 | 1691 | if (!PyArg_ParseTuple(args, "IOII", 1692 | &channel, &queue, &if_unused, &if_empty)) 1693 | goto bail; 1694 | if ((queue = PyObjectArray_Maybe_Unicode(queue, &pyobj_array)) == NULL) goto bail; 1695 | 1696 | Py_BEGIN_ALLOW_THREADS; 1697 | ok = amqp_queue_delete(self->conn, channel, 1698 | PyString_AS_AMQBYTES(queue), 1699 | (amqp_boolean_t)if_unused, 1700 | (amqp_boolean_t)if_empty); 1701 | if (ok == NULL) 1702 | reply = amqp_get_rpc_reply(self->conn); 1703 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 1704 | PyObjectArray_XDECREF(&pyobj_array); 1705 | Py_END_ALLOW_THREADS; 1706 | 1707 | if (ok == NULL && PyRabbitMQ_HandleAMQError(self, channel, 1708 | reply, "queue.delete")) 1709 | goto bail; 1710 | 1711 | return PyInt_FromLong((long)ok->message_count); 1712 | bail: 1713 | PyObjectArray_XDECREF(&pyobj_array); 1714 | return 0; 1715 | } 1716 | 1717 | 1718 | /* 1719 | * Connection._queue_declare 1720 | */ 1721 | static PyObject* 1722 | PyRabbitMQ_Connection_queue_declare(PyRabbitMQ_Connection *self, 1723 | PyObject *args) 1724 | { 1725 | PyObject *queue = NULL; 1726 | PyObject *arguments = NULL; 1727 | unsigned int channel = 0; 1728 | unsigned int passive = 0; 1729 | unsigned int durable = 0; 1730 | unsigned int exclusive = 0; 1731 | unsigned int auto_delete = 0; 1732 | 1733 | amqp_queue_declare_ok_t *ok; 1734 | amqp_rpc_reply_t reply; 1735 | amqp_pool_t *channel_pool = NULL; 1736 | amqp_table_t qargs = amqp_empty_table; 1737 | PyObject *ret = NULL; 1738 | 1739 | pyobject_array_t pyobj_array = {0}; 1740 | 1741 | if (PyRabbitMQ_Not_Connected(self)) 1742 | goto bail; 1743 | 1744 | if (!PyArg_ParseTuple(args, "IOIIIIO", 1745 | &channel, &queue, &passive, &durable, 1746 | &exclusive, &auto_delete, &arguments)) 1747 | goto bail; 1748 | if ((queue = PyObjectArray_Maybe_Unicode(queue, &pyobj_array)) == NULL) goto bail; 1749 | channel_pool = amqp_get_or_create_channel_pool(self->conn, (amqp_channel_t)channel); 1750 | if (channel_pool == NULL) { 1751 | PyErr_NoMemory(); 1752 | goto bail; 1753 | } 1754 | qargs = PyDict_ToAMQTable(self->conn, arguments, channel_pool, &pyobj_array); 1755 | if (PyErr_Occurred()) 1756 | goto bail; 1757 | 1758 | Py_BEGIN_ALLOW_THREADS; 1759 | ok = amqp_queue_declare(self->conn, channel, 1760 | PyString_AS_AMQBYTES(queue), 1761 | (amqp_boolean_t)passive, 1762 | (amqp_boolean_t)durable, 1763 | (amqp_boolean_t)exclusive, 1764 | (amqp_boolean_t)auto_delete, 1765 | qargs 1766 | ); 1767 | reply = amqp_get_rpc_reply(self->conn); 1768 | Py_END_ALLOW_THREADS; 1769 | PyObjectArray_XDECREF(&pyobj_array); 1770 | 1771 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "queue.declare")) 1772 | goto bail; 1773 | 1774 | if ((ret = PyTuple_New(3)) == NULL) goto bail; 1775 | PyTuple_SET_ITEM(ret, 0, PySTRING_FROM_AMQBYTES(ok->queue)); 1776 | PyTuple_SET_ITEM(ret, 1, PyInt_FromLong((long)ok->message_count)); 1777 | PyTuple_SET_ITEM(ret, 2, PyInt_FromLong((long)ok->consumer_count)); 1778 | return ret; 1779 | bail: 1780 | PyObjectArray_XDECREF(&pyobj_array); 1781 | 1782 | return 0; 1783 | } 1784 | 1785 | 1786 | /* 1787 | * Connection._queue_purge 1788 | */ 1789 | static PyObject* 1790 | PyRabbitMQ_Connection_queue_purge(PyRabbitMQ_Connection *self, 1791 | PyObject *args) 1792 | { 1793 | PyObject *queue = NULL; 1794 | unsigned int channel = 0; 1795 | unsigned int no_wait = 0; 1796 | 1797 | amqp_queue_purge_ok_t *ok; 1798 | amqp_rpc_reply_t reply; 1799 | 1800 | pyobject_array_t pyobj_array = {0}; 1801 | 1802 | if (PyRabbitMQ_Not_Connected(self)) 1803 | goto bail; 1804 | 1805 | if (!PyArg_ParseTuple(args, "IOI", &channel, &queue, &no_wait)) 1806 | goto bail; 1807 | if ((queue = PyObjectArray_Maybe_Unicode(queue, &pyobj_array)) == NULL) goto bail; 1808 | 1809 | Py_BEGIN_ALLOW_THREADS; 1810 | ok = amqp_queue_purge(self->conn, channel, 1811 | PyString_AS_AMQBYTES(queue)); 1812 | reply = amqp_get_rpc_reply(self->conn); 1813 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 1814 | PyObjectArray_XDECREF(&pyobj_array); 1815 | Py_END_ALLOW_THREADS; 1816 | 1817 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "queue.purge")) 1818 | goto bail; 1819 | 1820 | return PyInt_FromLong((long)ok->message_count); 1821 | bail: 1822 | PyObjectArray_XDECREF(&pyobj_array); 1823 | return 0; 1824 | } 1825 | 1826 | 1827 | /* 1828 | * Connection._exchange_declare 1829 | */ 1830 | static PyObject* 1831 | PyRabbitMQ_Connection_exchange_declare(PyRabbitMQ_Connection *self, 1832 | PyObject *args) 1833 | { 1834 | unsigned int channel = 0; 1835 | PyObject *exchange = NULL; 1836 | PyObject *type = NULL; 1837 | PyObject *arguments = 0; 1838 | unsigned int passive = 0; 1839 | unsigned int durable = 0; 1840 | /* auto_delete argument is ignored, 1841 | * as it has been decided that it's not that useful after all. */ 1842 | unsigned int auto_delete = 0; 1843 | 1844 | amqp_table_t eargs = amqp_empty_table; 1845 | amqp_pool_t *channel_pool = NULL; 1846 | amqp_rpc_reply_t reply; 1847 | 1848 | pyobject_array_t pyobj_array = {0}; 1849 | 1850 | if (PyRabbitMQ_Not_Connected(self)) 1851 | goto bail; 1852 | 1853 | if (!PyArg_ParseTuple(args, "IOOIIIO", 1854 | &channel, &exchange, &type, &passive, 1855 | &durable, &auto_delete, &arguments)) 1856 | goto bail; 1857 | if ((exchange = PyObjectArray_Maybe_Unicode(exchange, &pyobj_array)) == NULL) goto bail; 1858 | if ((type = PyObjectArray_Maybe_Unicode(type, &pyobj_array)) == NULL) goto bail; 1859 | 1860 | channel_pool = amqp_get_or_create_channel_pool(self->conn, (amqp_channel_t)channel); 1861 | if (channel_pool == NULL) { 1862 | PyErr_NoMemory(); 1863 | goto bail; 1864 | } 1865 | 1866 | eargs = PyDict_ToAMQTable(self->conn, arguments, channel_pool, &pyobj_array); 1867 | if (PyErr_Occurred()) 1868 | goto bail; 1869 | 1870 | Py_BEGIN_ALLOW_THREADS; 1871 | amqp_exchange_declare(self->conn, channel, 1872 | PyString_AS_AMQBYTES(exchange), 1873 | PyString_AS_AMQBYTES(type), 1874 | (amqp_boolean_t)passive, 1875 | (amqp_boolean_t)durable, 1876 | 0, 0, eargs 1877 | ); 1878 | reply = amqp_get_rpc_reply(self->conn); 1879 | Py_END_ALLOW_THREADS; 1880 | PyObjectArray_XDECREF(&pyobj_array); 1881 | 1882 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "exchange.declare")) 1883 | goto bail; 1884 | Py_RETURN_NONE; 1885 | bail: 1886 | PyObjectArray_XDECREF(&pyobj_array); 1887 | 1888 | return 0; 1889 | } 1890 | 1891 | /* 1892 | * Connection._exchange_delete 1893 | */ 1894 | static PyObject* 1895 | PyRabbitMQ_Connection_exchange_delete(PyRabbitMQ_Connection *self, 1896 | PyObject *args) 1897 | { 1898 | PyObject *exchange = NULL; 1899 | unsigned int channel = 0; 1900 | unsigned int if_unused = 0; 1901 | 1902 | amqp_rpc_reply_t reply; 1903 | 1904 | pyobject_array_t pyobj_array = {0}; 1905 | 1906 | if (PyRabbitMQ_Not_Connected(self)) 1907 | goto bail; 1908 | 1909 | if (!PyArg_ParseTuple(args, "IOI", &channel, &exchange, &if_unused)) 1910 | goto bail; 1911 | if ((exchange = PyObjectArray_Maybe_Unicode(exchange, &pyobj_array)) == NULL) goto bail; 1912 | 1913 | Py_BEGIN_ALLOW_THREADS; 1914 | amqp_exchange_delete(self->conn, channel, 1915 | PyString_AS_AMQBYTES(exchange), 1916 | (amqp_boolean_t)if_unused); 1917 | reply = amqp_get_rpc_reply(self->conn); 1918 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 1919 | Py_END_ALLOW_THREADS; 1920 | PyObjectArray_XDECREF(&pyobj_array); 1921 | 1922 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "exchange.delete")) 1923 | goto bail; 1924 | 1925 | Py_RETURN_NONE; 1926 | bail: 1927 | PyObjectArray_XDECREF(&pyobj_array); 1928 | 1929 | return 0; 1930 | } 1931 | 1932 | /* 1933 | * Connection._basic_publish 1934 | */ 1935 | static PyObject* 1936 | PyRabbitMQ_Connection_basic_publish(PyRabbitMQ_Connection *self, 1937 | PyObject *args) 1938 | { 1939 | PyObject *exchange = NULL; 1940 | PyObject *routing_key = NULL; 1941 | PyObject *propdict; 1942 | unsigned int channel = 0; 1943 | unsigned int mandatory = 0; 1944 | unsigned int immediate = 0; 1945 | 1946 | char *body_buf = NULL; 1947 | Py_ssize_t body_size = 0; 1948 | 1949 | int ret = 0; 1950 | amqp_basic_properties_t props; 1951 | amqp_bytes_t bytes; 1952 | amqp_pool_t *channel_pool = NULL; 1953 | memset(&props, 0, sizeof(props)); 1954 | 1955 | pyobject_array_t pyobj_array = {0}; 1956 | 1957 | if (PyRabbitMQ_Not_Connected(self)) 1958 | goto bail; 1959 | 1960 | if (!PyArg_ParseTuple(args, "Is#OOO|II", 1961 | &channel, &body_buf, &body_size, &exchange, &routing_key, 1962 | &propdict, &mandatory, &immediate)) 1963 | goto bail; 1964 | 1965 | if ((exchange = PyObjectArray_Maybe_Unicode(exchange, &pyobj_array)) == NULL) goto bail; 1966 | if ((routing_key = PyObjectArray_Maybe_Unicode(routing_key, &pyobj_array)) == NULL) goto bail; 1967 | 1968 | Py_INCREF(propdict); 1969 | channel_pool = amqp_get_or_create_channel_pool(self->conn, (amqp_channel_t)channel); 1970 | 1971 | if (PyDict_to_basic_properties(propdict, &props, self->conn, channel_pool, &pyobj_array) < 1) { 1972 | goto bail; 1973 | } 1974 | Py_DECREF(propdict); 1975 | 1976 | bytes.len = (size_t)body_size; 1977 | bytes.bytes = (void *)body_buf; 1978 | 1979 | Py_BEGIN_ALLOW_THREADS; 1980 | ret = amqp_basic_publish(self->conn, channel, 1981 | PyString_AS_AMQBYTES(exchange), 1982 | PyString_AS_AMQBYTES(routing_key), 1983 | (amqp_boolean_t)mandatory, 1984 | (amqp_boolean_t)immediate, 1985 | &props, 1986 | bytes); 1987 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 1988 | Py_END_ALLOW_THREADS; 1989 | PyObjectArray_XDECREF(&pyobj_array); 1990 | 1991 | if (!PyRabbitMQ_HandleError(ret, "basic.publish")) { 1992 | goto error; 1993 | } 1994 | Py_RETURN_NONE; 1995 | 1996 | error: 1997 | PyRabbitMQ_revive_channel(self, channel); 1998 | bail: 1999 | PyObjectArray_XDECREF(&pyobj_array); 2000 | 2001 | return 0; 2002 | } 2003 | 2004 | 2005 | /* 2006 | * Connection._basic_ack 2007 | */ 2008 | static PyObject* 2009 | PyRabbitMQ_Connection_basic_ack(PyRabbitMQ_Connection *self, 2010 | PyObject *args) 2011 | { 2012 | Py_ssize_t delivery_tag = 0; 2013 | unsigned int channel = 0; 2014 | unsigned int multiple = 0; 2015 | int ret = 0; 2016 | 2017 | if (PyRabbitMQ_Not_Connected(self)) 2018 | goto bail; 2019 | 2020 | if (!PyArg_ParseTuple(args, "InI", &channel, &delivery_tag, &multiple)) 2021 | goto bail; 2022 | 2023 | Py_BEGIN_ALLOW_THREADS; 2024 | ret = amqp_basic_ack(self->conn, channel, 2025 | (uint64_t)delivery_tag, 2026 | (amqp_boolean_t)multiple); 2027 | Py_END_ALLOW_THREADS; 2028 | 2029 | if (!PyRabbitMQ_HandleError(ret, "basic.ack")) 2030 | goto error; 2031 | 2032 | Py_RETURN_NONE; 2033 | error: 2034 | PyRabbitMQ_revive_channel(self, channel); 2035 | bail: 2036 | return 0; 2037 | } 2038 | 2039 | /* 2040 | * Connection._basic_reject 2041 | */ 2042 | static PyObject *PyRabbitMQ_Connection_basic_reject(PyRabbitMQ_Connection *self, 2043 | PyObject *args) 2044 | { 2045 | Py_ssize_t delivery_tag = 0; 2046 | unsigned int channel = 0; 2047 | unsigned int multiple = 0; 2048 | 2049 | int ret = 0; 2050 | 2051 | if (PyRabbitMQ_Not_Connected(self)) 2052 | goto bail; 2053 | 2054 | if (!PyArg_ParseTuple(args, "InI", &channel, &delivery_tag, &multiple)) 2055 | goto bail; 2056 | 2057 | Py_BEGIN_ALLOW_THREADS; 2058 | ret = amqp_basic_reject(self->conn, channel, 2059 | (uint64_t)delivery_tag, 2060 | (amqp_boolean_t)multiple); 2061 | Py_END_ALLOW_THREADS; 2062 | 2063 | if (!PyRabbitMQ_HandleError(ret, "basic.reject")) 2064 | goto error; 2065 | 2066 | Py_RETURN_NONE; 2067 | error: 2068 | PyRabbitMQ_revive_channel(self, channel); 2069 | bail: 2070 | return 0; 2071 | } 2072 | 2073 | 2074 | /* 2075 | * Connection._basic_cancel 2076 | */ 2077 | static PyObject* 2078 | PyRabbitMQ_Connection_basic_cancel(PyRabbitMQ_Connection *self, 2079 | PyObject *args) 2080 | { 2081 | PyObject *consumer_tag = NULL; 2082 | unsigned int channel = 0; 2083 | 2084 | amqp_rpc_reply_t reply; 2085 | 2086 | pyobject_array_t pyobj_array = {0}; 2087 | 2088 | if (PyRabbitMQ_Not_Connected(self)) 2089 | goto ok; 2090 | 2091 | if (!PyArg_ParseTuple(args, "IO", &channel, &consumer_tag)) 2092 | goto bail; 2093 | if ((consumer_tag = PyObjectArray_Maybe_Unicode(consumer_tag, &pyobj_array)) == NULL) goto bail; 2094 | 2095 | Py_BEGIN_ALLOW_THREADS; 2096 | amqp_basic_cancel(self->conn, channel, 2097 | PyString_AS_AMQBYTES(consumer_tag)); 2098 | reply = amqp_get_rpc_reply(self->conn); 2099 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 2100 | PyObjectArray_XDECREF(&pyobj_array); 2101 | Py_END_ALLOW_THREADS; 2102 | 2103 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "basic.cancel")) 2104 | goto bail; 2105 | 2106 | ok: 2107 | Py_RETURN_NONE; 2108 | bail: 2109 | PyObjectArray_XDECREF(&pyobj_array); 2110 | 2111 | return 0; 2112 | } 2113 | 2114 | 2115 | /* 2116 | * Connection._basic_consume 2117 | */ 2118 | static PyObject* 2119 | PyRabbitMQ_Connection_basic_consume(PyRabbitMQ_Connection *self, 2120 | PyObject *args) 2121 | { 2122 | PyObject *queue = NULL; 2123 | PyObject *consumer_tag = NULL; 2124 | PyObject *arguments = NULL; 2125 | unsigned int channel = 0; 2126 | unsigned int no_local = 0; 2127 | unsigned int no_ack = 0; 2128 | unsigned int exclusive = 0; 2129 | 2130 | amqp_basic_consume_ok_t *ok; 2131 | amqp_rpc_reply_t reply; 2132 | amqp_pool_t *channel_pool = NULL; 2133 | amqp_table_t cargs = amqp_empty_table; 2134 | 2135 | pyobject_array_t pyobj_array = {0}; 2136 | 2137 | if (PyRabbitMQ_Not_Connected(self)) 2138 | goto bail; 2139 | 2140 | if (!PyArg_ParseTuple(args, "IOOIIIO", 2141 | &channel, &queue, &consumer_tag, &no_local, 2142 | &no_ack, &exclusive, &arguments)) 2143 | goto bail; 2144 | if ((queue = PyObjectArray_Maybe_Unicode(queue, &pyobj_array)) == NULL) goto bail; 2145 | if ((consumer_tag = PyObjectArray_Maybe_Unicode(consumer_tag, &pyobj_array)) == NULL) goto bail; 2146 | 2147 | channel_pool = amqp_get_or_create_channel_pool(self->conn, (amqp_channel_t)channel); 2148 | if (channel_pool == NULL) { 2149 | PyErr_NoMemory(); 2150 | goto bail; 2151 | } 2152 | 2153 | cargs = PyDict_ToAMQTable(self->conn, arguments, channel_pool, &pyobj_array); 2154 | if (PyErr_Occurred()) 2155 | goto bail; 2156 | 2157 | Py_BEGIN_ALLOW_THREADS; 2158 | ok = amqp_basic_consume(self->conn, channel, 2159 | PyString_AS_AMQBYTES(queue), 2160 | PyString_AS_AMQBYTES(consumer_tag), 2161 | no_local, 2162 | no_ack, 2163 | exclusive, 2164 | cargs); 2165 | reply = amqp_get_rpc_reply(self->conn); 2166 | Py_END_ALLOW_THREADS; 2167 | PyObjectArray_XDECREF(&pyobj_array); 2168 | 2169 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "basic.consume")) 2170 | goto bail; 2171 | 2172 | return PySTRING_FROM_AMQBYTES(ok->consumer_tag); 2173 | bail: 2174 | PyObjectArray_XDECREF(&pyobj_array); 2175 | 2176 | return 0; 2177 | } 2178 | 2179 | /* 2180 | * Connection._basic_qos 2181 | */ 2182 | static PyObject* 2183 | PyRabbitMQ_Connection_basic_qos(PyRabbitMQ_Connection *self, 2184 | PyObject *args) 2185 | { 2186 | unsigned int channel = 0; 2187 | Py_ssize_t prefetch_size = 0; 2188 | unsigned int prefetch_count = 0; 2189 | unsigned int _global = 0; 2190 | 2191 | if (PyRabbitMQ_Not_Connected(self)) 2192 | goto error; 2193 | 2194 | if (!PyArg_ParseTuple(args, "InII", 2195 | &channel, &prefetch_size, &prefetch_count, &_global)) 2196 | goto error; 2197 | 2198 | Py_BEGIN_ALLOW_THREADS; 2199 | amqp_basic_qos(self->conn, channel, 2200 | (uint32_t)prefetch_size, 2201 | (uint16_t)prefetch_count, 2202 | (amqp_boolean_t)_global); 2203 | Py_END_ALLOW_THREADS; 2204 | 2205 | Py_RETURN_NONE; 2206 | error: 2207 | return 0; 2208 | } 2209 | 2210 | /* 2211 | * Connection._flow 2212 | */ 2213 | static PyObject* 2214 | PyRabbitMQ_Connection_flow(PyRabbitMQ_Connection *self, 2215 | PyObject *args) 2216 | { 2217 | unsigned int channel = 0; 2218 | unsigned int active = 1; 2219 | 2220 | amqp_rpc_reply_t reply; 2221 | 2222 | if (PyRabbitMQ_Not_Connected(self)) 2223 | goto bail; 2224 | 2225 | if (!PyArg_ParseTuple(args, "II", &channel, &active)) 2226 | goto bail; 2227 | 2228 | Py_BEGIN_ALLOW_THREADS; 2229 | amqp_channel_flow(self->conn, channel, (amqp_boolean_t)active); 2230 | reply = amqp_get_rpc_reply(self->conn); 2231 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 2232 | Py_END_ALLOW_THREADS; 2233 | 2234 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "channel.flow")) 2235 | goto bail; 2236 | 2237 | Py_RETURN_NONE; 2238 | bail: 2239 | return 0; 2240 | } 2241 | 2242 | /* 2243 | * Connection._basic_recover 2244 | */ 2245 | static PyObject* 2246 | PyRabbitMQ_Connection_basic_recover(PyRabbitMQ_Connection *self, 2247 | PyObject *args) 2248 | { 2249 | unsigned int channel = 0; 2250 | unsigned int requeue = 0; 2251 | 2252 | amqp_rpc_reply_t reply; 2253 | 2254 | if (PyRabbitMQ_Not_Connected(self)) 2255 | goto bail; 2256 | 2257 | if (!PyArg_ParseTuple(args, "II", &channel, &requeue)) 2258 | goto bail; 2259 | 2260 | Py_BEGIN_ALLOW_THREADS; 2261 | amqp_basic_recover(self->conn, channel, (amqp_boolean_t)requeue); 2262 | reply = amqp_get_rpc_reply(self->conn); 2263 | amqp_maybe_release_buffers_on_channel(self->conn, channel); 2264 | Py_END_ALLOW_THREADS; 2265 | 2266 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "basic.recover")) 2267 | goto bail; 2268 | 2269 | Py_RETURN_NONE; 2270 | bail: 2271 | return 0; 2272 | } 2273 | 2274 | 2275 | /* 2276 | * Connection._basic_recv 2277 | */ 2278 | static PyObject* 2279 | PyRabbitMQ_Connection_basic_recv(PyRabbitMQ_Connection *self, 2280 | PyObject *args) 2281 | { 2282 | int ready = 0; 2283 | double timeout; 2284 | 2285 | if (PyRabbitMQ_Not_Connected(self)) 2286 | goto bail; 2287 | 2288 | if (!PyArg_ParseTuple(args, "d", &timeout)) 2289 | goto bail; 2290 | 2291 | if (PYRMQ_SHOULD_POLL(timeout) && !AMQP_ACTIVE_BUFFERS(self->conn)) { 2292 | Py_BEGIN_ALLOW_THREADS; 2293 | ready = RabbitMQ_WAIT(self->sockfd, timeout); 2294 | Py_END_ALLOW_THREADS; 2295 | if (PyRabbitMQ_HandlePollError(ready) <= 0) 2296 | goto bail; 2297 | } 2298 | 2299 | if (PyRabbitMQ_recv(self, NULL, self->conn, 0) < 0) { 2300 | if (!PyErr_Occurred()) 2301 | PyErr_SetString(PyRabbitMQExc_ConnectionError, "Bad frame read"); 2302 | goto error; 2303 | } 2304 | 2305 | Py_RETURN_NONE; 2306 | error: 2307 | PyRabbitMQ_Connection_close(self); 2308 | bail: 2309 | return 0; 2310 | } 2311 | 2312 | /* 2313 | * Connection.__repr__ 2314 | */ 2315 | static PyObject * 2316 | PyRabbitMQ_Connection_repr(PyRabbitMQ_Connection *self) 2317 | { 2318 | return FROM_FORMAT("", 2319 | self->userid, self->password, self->hostname, 2320 | self->port, self->virtual_host); 2321 | } 2322 | 2323 | 2324 | /* Connection._basic_get */ 2325 | static PyObject* 2326 | PyRabbitMQ_Connection_basic_get(PyRabbitMQ_Connection *self, 2327 | PyObject *args) 2328 | { 2329 | PyObject *queue = NULL; 2330 | unsigned int channel = 0; 2331 | unsigned int no_ack = 0; 2332 | 2333 | amqp_rpc_reply_t reply; 2334 | amqp_basic_get_ok_t *ok = NULL; 2335 | PyObject *p = NULL; 2336 | PyObject *delivery_info = NULL; 2337 | PyObject *message_count = NULL; 2338 | 2339 | pyobject_array_t pyobj_array = {0}; 2340 | 2341 | if (PyRabbitMQ_Not_Connected(self)) 2342 | goto bail; 2343 | 2344 | if (!PyArg_ParseTuple(args, "IOI", &channel, &queue, &no_ack)) 2345 | goto bail; 2346 | if ((queue = PyObjectArray_Maybe_Unicode(queue, &pyobj_array)) == NULL) goto bail; 2347 | 2348 | Py_BEGIN_ALLOW_THREADS; 2349 | reply = amqp_basic_get(self->conn, channel, 2350 | PyString_AS_AMQBYTES(queue), 2351 | (amqp_boolean_t)no_ack); 2352 | PyObjectArray_XDECREF(&pyobj_array); 2353 | Py_END_ALLOW_THREADS; 2354 | 2355 | if (PyRabbitMQ_HandleAMQError(self, channel, reply, "basic.get")) 2356 | goto bail; 2357 | if (reply.reply.id != AMQP_BASIC_GET_OK_METHOD) 2358 | goto empty; 2359 | 2360 | ok = (amqp_basic_get_ok_t *)reply.reply.decoded; 2361 | p = PyDict_New(); 2362 | 2363 | /* p["delivery_info"] = {} */ 2364 | delivery_info = PyDict_New(); 2365 | PyDICT_SETSTR_DECREF(p, "delivery_info", delivery_info); 2366 | amqp_basic_deliver_to_PyDict(delivery_info, 2367 | ok->delivery_tag, 2368 | ok->exchange, 2369 | ok->routing_key, 2370 | ok->redelivered); 2371 | /* add in the message_count */ 2372 | message_count = PyLong_FromLong(ok->message_count); 2373 | PyDict_SetItemString(delivery_info, "message_count", message_count); 2374 | Py_XDECREF(message_count); 2375 | 2376 | if (amqp_data_in_buffer(self->conn)) { 2377 | if (PyRabbitMQ_recv(self, p, self->conn, 1) < 0) { 2378 | if (!PyErr_Occurred()) 2379 | PyErr_SetString(PyRabbitMQExc_ConnectionError, 2380 | "Bad frame read"); 2381 | Py_XDECREF(p); 2382 | Py_XDECREF(delivery_info); 2383 | goto error; 2384 | } 2385 | } 2386 | return p; 2387 | error: 2388 | PyRabbitMQ_Connection_close(self); 2389 | bail: 2390 | PyObjectArray_XDECREF(&pyobj_array); 2391 | 2392 | return 0; 2393 | empty: 2394 | Py_RETURN_NONE; 2395 | } 2396 | 2397 | 2398 | /* Module: _librabbitmq */ 2399 | 2400 | static PyMethodDef PyRabbitMQ_functions[] = { 2401 | {NULL, NULL, 0, NULL} 2402 | }; 2403 | 2404 | #if PY_MAJOR_VERSION >= 3 2405 | static struct PyModuleDef PyRabbitMQ_moduledef = { 2406 | PyModuleDef_HEAD_INIT, 2407 | PYRABBITMQ_MODULE_NAME, /* m_name */ 2408 | PYRABBITMQ_MODULE_DESC, /* m_doc */ 2409 | -1, /* m_size */ 2410 | PyRabbitMQ_functions, /* m_methods */ 2411 | NULL, /* m_reload */ 2412 | NULL, /* m_traverse */ 2413 | NULL, /* m_clear */ 2414 | NULL, /* m_free */ 2415 | }; 2416 | #endif 2417 | 2418 | PYRABBITMQ_MOD_INIT(_librabbitmq) 2419 | { 2420 | PyObject *module, *socket_module; 2421 | 2422 | if (PyType_Ready(&PyRabbitMQ_ConnectionType) < 0) { 2423 | #if PY_MAJOR_VERSION >= 3 2424 | return NULL; 2425 | #else 2426 | return; 2427 | #endif 2428 | } 2429 | 2430 | #if PY_MAJOR_VERSION >= 3 2431 | module = PyModule_Create(&PyRabbitMQ_moduledef); 2432 | #else 2433 | module = Py_InitModule3(PYRABBITMQ_MODULE_NAME, PyRabbitMQ_functions, 2434 | PYRABBITMQ_MODULE_DESC); 2435 | #endif 2436 | 2437 | if (module == NULL) { 2438 | #if PY_MAJOR_VERSION >= 3 2439 | return NULL; 2440 | #else 2441 | return; 2442 | #endif 2443 | } 2444 | 2445 | /* Get socket.error */ 2446 | socket_module = PyImport_ImportModule("socket"); 2447 | if (!socket_module) { 2448 | #if PY_MAJOR_VERSION >= 3 2449 | return NULL; 2450 | #else 2451 | return; 2452 | #endif 2453 | } 2454 | PyRabbitMQ_socket_timeout = PyObject_GetAttrString(socket_module, "timeout"); 2455 | Py_XDECREF(socket_module); 2456 | 2457 | PyModule_AddStringConstant(module, "__version__", PYRABBITMQ_VERSION); 2458 | PyModule_AddStringConstant(module, "__author__", PYRABBITMQ_AUTHOR); 2459 | PyModule_AddStringConstant(module, "__contact__", PYRABBITMQ_CONTACT); 2460 | PyModule_AddStringConstant(module, "__homepage__", PYRABBITMQ_HOMEPAGE); 2461 | 2462 | Py_INCREF(&PyRabbitMQ_ConnectionType); 2463 | PyModule_AddObject(module, "Connection", (PyObject *)&PyRabbitMQ_ConnectionType); 2464 | 2465 | PyModule_AddIntConstant(module, "AMQP_SASL_METHOD_PLAIN", AMQP_SASL_METHOD_PLAIN); 2466 | 2467 | PyRabbitMQExc_ConnectionError = PyErr_NewException( 2468 | "_librabbitmq.ConnectionError", NULL, NULL); 2469 | PyModule_AddObject(module, "ConnectionError", 2470 | (PyObject *)PyRabbitMQExc_ConnectionError); 2471 | PyRabbitMQExc_ChannelError = PyErr_NewException( 2472 | "_librabbitmq.ChannelError", NULL, NULL); 2473 | PyModule_AddObject(module, "ChannelError", 2474 | (PyObject *)PyRabbitMQExc_ChannelError); 2475 | #if PY_MAJOR_VERSION >= 3 2476 | return module; 2477 | #else 2478 | return; 2479 | #endif 2480 | } 2481 | --------------------------------------------------------------------------------