You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by mm...@apache.org on 2022/09/30 19:35:05 UTC
[pulsar-client-python] branch main updated: PIP-209: Compile Python client wrapper (#1)
This is an automated email from the ASF dual-hosted git repository.
mmerli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pulsar-client-python.git
The following commit(s) were added to refs/heads/main by this push:
new 9f6fc69 PIP-209: Compile Python client wrapper (#1)
9f6fc69 is described below
commit 9f6fc69987fa93f44897d275dbe8e6b505df9abb
Author: Matteo Merli <mm...@apache.org>
AuthorDate: Fri Sep 30 12:35:00 2022 -0700
PIP-209: Compile Python client wrapper (#1)
* PIP-209: Compile Python client wrapper
* Removed Optional
* Fixed CMakefile
* Added __pycache__ to gitignore
* Added clang-format support
---
.clang-format | 25 +++++
.gitignore | 7 ++
CMakeLists.txt | 108 +++++++++++++++++-
build-mac-wheels.sh | 54 +++++++--
build-support/clang_format_exclusions.txt | 18 +++
build-support/run_clang_format.py | 80 +++++++++++++
cmake_modules/FindClangTools.cmake | 100 +++++++++++++++++
setup.py | 30 ++---
src/config.cc | 1 -
src/future.h | 181 ++++++++++++++++++++++++++++++
src/utils.h | 25 ++++-
version.txt | 5 +
12 files changed, 596 insertions(+), 38 deletions(-)
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..cb40b50
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+BasedOnStyle: Google
+IndentWidth: 4
+ColumnLimit: 110
+SortIncludes: false
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterEnum: true
diff --git a/.gitignore b/.gitignore
index 5cb909f..1a4bc13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,10 @@ MANIFEST
build
dist
*.egg-info
+.idea
+CMakeCache.txt
+CMakeFiles
+Makefile
+_pulsar.so
+cmake_install.cmake
+__pycache__
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 63cf163..6c994b2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,7 +17,81 @@
# under the License.
#
-INCLUDE_DIRECTORIES("${Boost_INCLUDE_DIRS}" "${PYTHON_INCLUDE_DIRS}")
+project (pulsar-client-python)
+cmake_minimum_required(VERSION 3.12)
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules")
+
+MESSAGE(STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE})
+set(THREADS_PREFER_PTHREAD_FLAG TRUE)
+find_package(Threads REQUIRED)
+MESSAGE(STATUS "Threads library: " ${CMAKE_THREAD_LIBS_INIT})
+
+
+find_library(PULSAR_LIBRARY NAMES libpulsar.a)
+message(STATUS "PULSAR_LIBRARY: ${PULSAR_LIBRARY}")
+
+find_path(PULSAR_INCLUDE pulsar/Client.h)
+message(STATUS "PULSAR_INCLUDE: ${PULSAR_INCLUDE}")
+
+SET(Boost_NO_BOOST_CMAKE ON)
+SET(Boost_USE_STATIC_LIBS ON)
+
+SET(CMAKE_CXX_STANDARD 11)
+
+find_package(Boost)
+
+find_package (Python3 COMPONENTS Development)
+MESSAGE(STATUS "PYTHON: " ${Python3_VERSION} " - " ${Python3_INCLUDE_DIRS})
+
+string(REPLACE "." ";" PYTHONLIBS_VERSION_NO_LIST ${Python3_VERSION})
+
+set(BOOST_PYTHON_NAME_POSTFIX ${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR})
+# For python3 the lib name is boost_python3
+set(BOOST_PYTHON_NAME_LIST python${BOOST_PYTHON_NAME_POSTFIX};python37;python38;python39;python310;python3;python3-mt;python-py${BOOST_PYTHON_NAME_POSTFIX};python${BOOST_PYTHON_NAME_POSTFIX}-mt)
+
+foreach (BOOST_PYTHON_NAME IN LISTS BOOST_PYTHON_NAME_LIST)
+ find_package(Boost QUIET COMPONENTS ${BOOST_PYTHON_NAME})
+ if (${Boost_FOUND})
+ set(BOOST_PYTHON_NAME_FOUND ${BOOST_PYTHON_NAME})
+ break()
+ endif()
+endforeach()
+
+if (NOT ${Boost_FOUND})
+ MESSAGE(FATAL_ERROR "Could not find Boost Python library")
+endif ()
+
+MESSAGE(STATUS "BOOST_PYTHON_NAME_FOUND: " ${BOOST_PYTHON_NAME_FOUND})
+
+set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT_DIR} /usr/lib64/)
+
+### This part is to find and keep SSL dynamic libs in RECORD_OPENSSL_SSL_LIBRARY and RECORD_OPENSSL_CRYPTO_LIBRARY
+### After find the libs, will unset related cache, and will not affect another same call to find_package.
+if (APPLE)
+ set(OPENSSL_INCLUDE_DIR /usr/local/opt/openssl/include/ /opt/homebrew/opt/openssl/include)
+ set(OPENSSL_ROOT_DIR ${OPENSSL_ROOT_DIR} /usr/local/opt/openssl/ /opt/homebrew/opt/openssl)
+endif ()
+
+set(OPENSSL_USE_STATIC_LIBS TRUE)
+find_package(OpenSSL REQUIRED)
+
+find_library(ZLIB_LIBRARIES REQUIRED NAMES libz.a z zlib)
+message(STATUS "ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}")
+
+find_library(CURL_LIBRARIES NAMES libcurl.a curl curl_a libcurl_a)
+message(STATUS "CURL_LIBRARIES: ${CURL_LIBRARIES}")
+find_library(Protobuf_LIBRARIES NAMES libprotobuf.a libprotobuf)
+message(STATUS "Protobuf: ${Protobuf_LIBRARIES}")
+find_library(CURL_LIBRARIES NAMES libcurl.a curl curl_a libcurl_a)
+message(STATUS "CURL_LIBRARIES: ${CURL_LIBRARIES}")
+find_library(LIB_ZSTD NAMES libzstd.a)
+message(STATUS "ZStd: ${LIB_ZSTD}")
+find_library(LIB_SNAPPY NAMES libsnappy.a)
+message(STATUS "LIB_SNAPPY: ${LIB_SNAPPY}")
+
+########################################################################################################################
+
+INCLUDE_DIRECTORIES(${PULSAR_INCLUDE} "${Boost_INCLUDE_DIRS}" "${Python3_INCLUDE_DIRS}")
ADD_LIBRARY(_pulsar SHARED src/pulsar.cc
src/producer.cc
@@ -63,7 +137,14 @@ if (NOT DEFINED ${Boost_PYTHON310-MT_LIBRARY})
endif()
# Try all possible boost-python variable namings
-set(PYTHON_WRAPPER_LIBS ${Boost_PYTHON_LIBRARY}
+set(PYTHON_WRAPPER_LIBS ${PULSAR_LIBRARY}
+ ${OPENSSL_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ ${CURL_LIBRARIES}
+ ${Protobuf_LIBRARIES}
+ ${LIB_ZSTD}
+ ${LIB_SNAPPY}
+ ${Boost_PYTHON_LIBRARY}
${Boost_PYTHON3_LIBRARY}
${Boost_PYTHON37-MT_LIBRARY}
${Boost_PYTHON38_LIBRARY}
@@ -86,18 +167,33 @@ if (APPLE)
endif ()
endif()
-message(STATUS "Using Boost Python libs: ${PYTHON_WRAPPER_LIBS}")
-
if (NOT PYTHON_WRAPPER_LIBS)
MESSAGE(FATAL_ERROR "Could not find Boost Python library")
endif ()
+message(STATUS "All libraries: ${PYTHON_WRAPPER_LIBS}")
+
if (APPLE)
set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS} -undefined dynamic_lookup")
- target_link_libraries(_pulsar -Wl,-all_load pulsarStatic ${PYTHON_WRAPPER_LIBS} ${COMMON_LIBS} ${ICU_LIBS})
+ target_link_libraries(_pulsar -Wl,-all_load ${PYTHON_WRAPPER_LIBS})
else ()
if (NOT MSVC)
set (CMAKE_SHARED_LINKER_FLAGS " -static-libgcc -static-libstdc++")
endif()
- target_link_libraries(_pulsar pulsarStatic ${PYTHON_WRAPPER_LIBS} ${COMMON_LIBS})
+ target_link_libraries(_pulsar ${PYTHON_WRAPPER_LIBS})
endif ()
+
+find_package(ClangTools)
+set(BUILD_SUPPORT_DIR "${CMAKE_SOURCE_DIR}/build-support")
+add_custom_target(format ${BUILD_SUPPORT_DIR}/run_clang_format.py
+ ${CLANG_FORMAT_BIN}
+ 0
+ ${BUILD_SUPPORT_DIR}/clang_format_exclusions.txt
+ ${CMAKE_SOURCE_DIR}/src)
+
+# `make check-format` option (for CI test)
+add_custom_target(check-format ${BUILD_SUPPORT_DIR}/run_clang_format.py
+ ${CLANG_FORMAT_BIN}
+ 1
+ ${BUILD_SUPPORT_DIR}/clang_format_exclusions.txt
+ ${CMAKE_SOURCE_DIR}/src)
diff --git a/build-mac-wheels.sh b/build-mac-wheels.sh
index 6a4dae7..38072f6 100755
--- a/build-mac-wheels.sh
+++ b/build-mac-wheels.sh
@@ -39,8 +39,9 @@ SNAPPY_VERSION=1.1.3
CURL_VERSION=7.61.0
ROOT_DIR=$(git rev-parse --show-toplevel)
-cd "${ROOT_DIR}/pulsar-client-cpp"
+cd "${ROOT_DIR}"
+PULSAR_VERSION=$(cat version.txt | grep pulsar-client-cpp | awk '{print $2}')
# Compile and cache dependencies
CACHE_DIR=~/.pulsar-mac-wheels-cache
@@ -246,6 +247,43 @@ else
echo "Using cached LibCurl"
fi
+###############################################################################
+if [ ! -f apache-pulsar-${PULSAR_VERSION}-src/.done ]; then
+ echo "Building Pulsar C++ client - ${PULSAR_VERSION}"
+ curl -O -L https://archive.apache.org/dist/pulsar/pulsar-${PULSAR_VERSION}/apache-pulsar-${PULSAR_VERSION}-src.tar.gz
+ rm -rf apache-pulsar-${PULSAR_VERSION}-src/pulsar-client-cpp
+ tar xfz apache-pulsar-${PULSAR_VERSION}-src.tar.gz
+ pushd apache-pulsar-${PULSAR_VERSION}-src
+ pushd pulsar-client-cpp
+ ARCHS='arm64;x86_64'
+
+ chmod +x build-support/merge_archives.sh
+ set -x
+ cmake . \
+ -DCMAKE_OSX_ARCHITECTURES=${ARCHS} \
+ -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \
+ -DCMAKE_INSTALL_PREFIX=$PREFIX \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_PREFIX_PATH=$PREFIX \
+ -DCMAKE_CXX_FLAGS=-I$PREFIX/include \
+ -DBoost_INCLUDE_DIR=$CACHE_DIR/boost-py-$PYTHON_VERSION/include \
+ -DBoost_LIBRARY_DIR=$CACHE_DIR/boost-py-$PYTHON_VERSION/lib \
+ -DLINK_STATIC=OFF \
+ -DBUILD_TESTS=OFF \
+ -DBUILD_PYTHON_WRAPPER=OFF \
+ -DBUILD_WIRESHARK=OFF \
+ -DBUILD_DYNAMIC_LIB=OFF \
+ -DBUILD_STATIC_LIB=ON \
+ -DPROTOC_PATH=$PREFIX/bin/protoc
+
+ make -j16 install
+ popd
+ touch .done
+ popd
+else
+ echo "Using cached Pulsar C++ client"
+fi
+
###############################################################################
###############################################################################
###############################################################################
@@ -260,7 +298,7 @@ for line in "${PYTHON_VERSIONS[@]}"; do
echo '----------------------------------------------------------------------------'
echo "Build wheel for Python $PYTHON_VERSION"
- cd "${ROOT_DIR}/pulsar-client-cpp"
+ cd "${ROOT_DIR}"
find . -name CMakeCache.txt | xargs -r rm
find . -name CMakeFiles | xargs -r rm -rf
@@ -285,16 +323,12 @@ for line in "${PYTHON_VERSIONS[@]}"; do
-DCMAKE_CXX_FLAGS=-I$PREFIX/include \
-DBoost_INCLUDE_DIR=$CACHE_DIR/boost-py-$PYTHON_VERSION/include \
-DBoost_LIBRARY_DIR=$CACHE_DIR/boost-py-$PYTHON_VERSION/lib \
- -DPYTHON_INCLUDE_DIR=$PY_INCLUDE_DIR \
- -DPYTHON_LIBRARY=$PY_PREFIX/lib/libpython${PYTHON_VERSION}.dylib \
- -DLINK_STATIC=ON \
- -DBUILD_TESTS=OFF \
- -DBUILD_WIRESHARK=OFF \
- -DPROTOC_PATH=$PREFIX/bin/protoc
+ -DPython3_INCLUDE_DIR=$PY_INCLUDE_DIR \
+ -DPython3_LIBRARY=$PY_PREFIX/lib/libpython${PYTHON_VERSION}.dylib \
+ -DPULSAR_INCLUDE=${PREFIX}/include
make clean
- make _pulsar -j16
+ make -j16
- cd python
$PY_EXE setup.py bdist_wheel
done
diff --git a/build-support/clang_format_exclusions.txt b/build-support/clang_format_exclusions.txt
new file mode 100644
index 0000000..fe95886
--- /dev/null
+++ b/build-support/clang_format_exclusions.txt
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
diff --git a/build-support/run_clang_format.py b/build-support/run_clang_format.py
new file mode 100755
index 0000000..3c99494
--- /dev/null
+++ b/build-support/run_clang_format.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Original: https://github.com/apache/arrow/blob/4dbce607d50031a405af39d36e08cd03c5ffc764/cpp/build-support/run_clang_format.py
+# ChangeLog:
+# 2018-01-08: Accept multiple source directories (@Licht-T)
+
+import fnmatch
+import os
+import subprocess
+import sys
+
+if len(sys.argv) < 5:
+ sys.stderr.write("Usage: %s $CLANG_FORMAT $CHECK_FORMAT exclude_globs.txt "
+ "$source_dir1 $source_dir2\n" %
+ sys.argv[0])
+ sys.exit(1)
+
+CLANG_FORMAT = sys.argv[1]
+CHECK_FORMAT = int(sys.argv[2]) == 1
+EXCLUDE_GLOBS_FILENAME = sys.argv[3]
+SOURCE_DIRS = sys.argv[4:]
+
+exclude_globs = [line.strip() for line in open(EXCLUDE_GLOBS_FILENAME, "r")]
+
+files_to_format = []
+matches = []
+for source_dir in SOURCE_DIRS:
+ for directory, subdirs, files in os.walk(source_dir):
+ for name in files:
+ name = os.path.join(directory, name)
+ if not (name.endswith('.h') or name.endswith('.cc')):
+ continue
+
+ excluded = False
+ for g in exclude_globs:
+ if fnmatch.fnmatch(name, g):
+ excluded = True
+ break
+ if not excluded:
+ files_to_format.append(name)
+
+if CHECK_FORMAT:
+ output = subprocess.check_output([CLANG_FORMAT, '-output-replacements-xml']
+ + files_to_format,
+ stderr=subprocess.STDOUT).decode('utf8')
+
+ to_fix = []
+ for line in output.split('\n'):
+ if 'offset' in line:
+ to_fix.append(line)
+
+ if len(to_fix) > 0:
+ print("clang-format checks failed, run 'make format' to fix")
+ sys.exit(-1)
+else:
+ try:
+ cmd = [CLANG_FORMAT, '-i'] + files_to_format
+ subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ except Exception as e:
+ print(e)
+ print(' '.join(cmd))
+ raise
\ No newline at end of file
diff --git a/cmake_modules/FindClangTools.cmake b/cmake_modules/FindClangTools.cmake
new file mode 100644
index 0000000..4b8fc18
--- /dev/null
+++ b/cmake_modules/FindClangTools.cmake
@@ -0,0 +1,100 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Tries to find the clang-tidy and clang-format modules
+#
+# Usage of this module as follows:
+#
+# find_package(ClangTools)
+#
+# Variables used by this module, they can change the default behaviour and need
+# to be set before calling find_package:
+#
+# ClangToolsBin_HOME -
+# When set, this path is inspected instead of standard library binary locations
+# to find clang-tidy and clang-format
+#
+# This module defines
+# CLANG_TIDY_BIN, The path to the clang tidy binary
+# CLANG_TIDY_FOUND, Whether clang tidy was found
+# CLANG_FORMAT_BIN, The path to the clang format binary
+# CLANG_TIDY_FOUND, Whether clang format was found
+
+list(APPEND CLANG_SEARCH_PATHS ${ClangTools_PATH} $ENV{CLANG_TOOLS_PATH} /usr/local/bin /usr/bin /opt/homebrew/bin)
+if (WIN32)
+ list(APPEND CLANG_SEARCH_PATHS "C:/Program Files/LLVM/bin" "C:/Program Files (x86)/LLVM/bin")
+endif()
+
+find_program(CLANG_TIDY_BIN
+ NAMES clang-tidy-4.0
+ clang-tidy-3.9
+ clang-tidy-3.8
+ clang-tidy-3.7
+ clang-tidy-3.6
+ clang-tidy
+ PATHS ${CLANG_SEARCH_PATHS}
+ NO_DEFAULT_PATH
+ )
+
+if ( "${CLANG_TIDY_BIN}" STREQUAL "CLANG_TIDY_BIN-NOTFOUND" )
+ set(CLANG_TIDY_FOUND 0)
+ message("clang-tidy not found")
+else()
+ set(CLANG_TIDY_FOUND 1)
+ message("clang-tidy found at ${CLANG_TIDY_BIN}")
+endif()
+
+if (CLANG_FORMAT_VERSION)
+ find_program(CLANG_FORMAT_BIN
+ NAMES clang-format-${CLANG_FORMAT_VERSION}
+ PATHS ${CLANG_SEARCH_PATHS}
+ NO_DEFAULT_PATH
+ )
+
+ # If not found yet, search alternative locations
+ if (("${CLANG_FORMAT_BIN}" STREQUAL "CLANG_FORMAT_BIN-NOTFOUND") AND APPLE)
+ # Homebrew ships older LLVM versions in /usr/local/opt/llvm@version/
+ STRING(REGEX REPLACE "^([0-9]+)\\.[0-9]+" "\\1" CLANG_FORMAT_MAJOR_VERSION "${CLANG_FORMAT_VERSION}")
+ STRING(REGEX REPLACE "^[0-9]+\\.([0-9]+)" "\\1" CLANG_FORMAT_MINOR_VERSION "${CLANG_FORMAT_VERSION}")
+ if ("${CLANG_FORMAT_MINOR_VERSION}" STREQUAL "0")
+ find_program(CLANG_FORMAT_BIN
+ NAMES clang-format
+ PATHS /usr/local/opt/llvm@${CLANG_FORMAT_MAJOR_VERSION}/bin
+ NO_DEFAULT_PATH
+ )
+ else()
+ find_program(CLANG_FORMAT_BIN
+ NAMES clang-format
+ PATHS /usr/local/opt/llvm@${CLANG_FORMAT_VERSION}/bin
+ NO_DEFAULT_PATH
+ )
+ endif()
+ endif()
+else()
+ find_program(CLANG_FORMAT_BIN
+ NAMES clang-format-5
+ clang-format-5.0
+ clang-format
+ PATHS ${CLANG_SEARCH_PATHS}
+ NO_DEFAULT_PATH
+ )
+endif()
+
+if ( "${CLANG_FORMAT_BIN}" STREQUAL "CLANG_FORMAT_BIN-NOTFOUND" )
+ set(CLANG_FORMAT_FOUND 0)
+ message("clang-format not found")
+else()
+ set(CLANG_FORMAT_FOUND 1)
+ message("clang-format found at ${CLANG_FORMAT_BIN}")
+endif()
+
diff --git a/setup.py b/setup.py
index 684d809..1263df4 100644
--- a/setup.py
+++ b/setup.py
@@ -19,29 +19,19 @@
from setuptools import setup
from distutils.core import Extension
-from distutils.util import strtobool
-from os import environ
+from os import environ, path
from distutils.command import build_ext
-import xml.etree.ElementTree as ET
-from os.path import dirname, realpath, join
def get_version():
- use_full_pom_name = strtobool(environ.get('USE_FULL_POM_NAME', 'False'))
-
- # Get the pulsar version from pom.xml
- TOP_LEVEL_PATH = dirname(dirname(dirname(realpath(__file__))))
- POM_PATH = join(TOP_LEVEL_PATH, 'pom.xml')
- root = ET.XML(open(POM_PATH).read())
- version = root.find('{http://maven.apache.org/POM/4.0.0}version').text.strip()
-
- if use_full_pom_name:
- return version
- else:
- # Strip the '-incubating' suffix, since it prevents the packages
- # from being uploaded into PyPI
- return version.split('-')[0]
+ # Get the pulsar version from version.txt
+ root = path.dirname(path.realpath(__file__))
+ version_file = path.join(root, 'version.txt')
+ with open(version_file) as f:
+ for line in f.readlines():
+ if 'pulsar-client-python: ' in line:
+ return line.split()[-1].strip()
def get_name():
@@ -53,8 +43,8 @@ def get_name():
VERSION = get_version()
NAME = get_name()
-print(VERSION)
-print(NAME)
+print('NAME: %s' % NAME)
+print('VERSION: %s' % VERSION)
# This is a workaround to have setuptools to include
diff --git a/src/config.cc b/src/config.cc
index fed9c28..c312648 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -18,7 +18,6 @@
*/
#include "utils.h"
#include <pulsar/ConsoleLoggerFactory.h>
-#include "lib/Utils.h"
#include <memory>
template <typename T>
diff --git a/src/future.h b/src/future.h
new file mode 100644
index 0000000..6754c89
--- /dev/null
+++ b/src/future.h
@@ -0,0 +1,181 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef LIB_FUTURE_H_
+#define LIB_FUTURE_H_
+
+#include <functional>
+#include <mutex>
+#include <memory>
+#include <condition_variable>
+
+#include <list>
+
+typedef std::unique_lock<std::mutex> Lock;
+
+namespace pulsar {
+
+template <typename Result, typename Type>
+struct InternalState {
+ std::mutex mutex;
+ std::condition_variable condition;
+ Result result;
+ Type value;
+ bool complete;
+
+ std::list<typename std::function<void(Result, const Type&)> > listeners;
+};
+
+template <typename Result, typename Type>
+class Future {
+ public:
+ typedef std::function<void(Result, const Type&)> ListenerCallback;
+
+ Future& addListener(ListenerCallback callback) {
+ InternalState<Result, Type>* state = state_.get();
+ Lock lock(state->mutex);
+
+ if (state->complete) {
+ lock.unlock();
+ callback(state->result, state->value);
+ } else {
+ state->listeners.push_back(callback);
+ }
+
+ return *this;
+ }
+
+ Result get(Type& result) {
+ InternalState<Result, Type>* state = state_.get();
+ Lock lock(state->mutex);
+
+ if (!state->complete) {
+ // Wait for result
+ while (!state->complete) {
+ state->condition.wait(lock);
+ }
+ }
+
+ result = state->value;
+ return state->result;
+ }
+
+ template <typename Duration>
+ bool get(Result& res, Type& value, Duration d) {
+ InternalState<Result, Type>* state = state_.get();
+ Lock lock(state->mutex);
+
+ if (!state->complete) {
+ // Wait for result
+ while (!state->complete) {
+ if (!state->condition.wait_for(lock, d, [&state] { return state->complete; })) {
+ // Timeout while waiting for the future to complete
+ return false;
+ }
+ }
+ }
+
+ value = state->value;
+ res = state->result;
+ return true;
+ }
+
+ private:
+ typedef std::shared_ptr<InternalState<Result, Type> > InternalStatePtr;
+ Future(InternalStatePtr state) : state_(state) {}
+
+ std::shared_ptr<InternalState<Result, Type> > state_;
+
+ template <typename U, typename V>
+ friend class Promise;
+};
+
+template <typename Result, typename Type>
+class Promise {
+ public:
+ Promise() : state_(std::make_shared<InternalState<Result, Type> >()) {}
+
+ bool setValue(const Type& value) const {
+ static Result DEFAULT_RESULT;
+ InternalState<Result, Type>* state = state_.get();
+ Lock lock(state->mutex);
+
+ if (state->complete) {
+ return false;
+ }
+
+ state->value = value;
+ state->result = DEFAULT_RESULT;
+ state->complete = true;
+
+ decltype(state->listeners) listeners;
+ listeners.swap(state->listeners);
+
+ lock.unlock();
+
+ for (auto& callback : listeners) {
+ callback(DEFAULT_RESULT, value);
+ }
+
+ state->condition.notify_all();
+ return true;
+ }
+
+ bool setFailed(Result result) const {
+ static Type DEFAULT_VALUE;
+ InternalState<Result, Type>* state = state_.get();
+ Lock lock(state->mutex);
+
+ if (state->complete) {
+ return false;
+ }
+
+ state->result = result;
+ state->complete = true;
+
+ decltype(state->listeners) listeners;
+ listeners.swap(state->listeners);
+
+ lock.unlock();
+
+ for (auto& callback : listeners) {
+ callback(result, DEFAULT_VALUE);
+ }
+
+ state->condition.notify_all();
+ return true;
+ }
+
+ bool isComplete() const {
+ InternalState<Result, Type>* state = state_.get();
+ Lock lock(state->mutex);
+ return state->complete;
+ }
+
+ Future<Result, Type> getFuture() const { return Future<Result, Type>(state_); }
+
+ private:
+ typedef std::function<void(Result, const Type&)> ListenerCallback;
+ std::shared_ptr<InternalState<Result, Type> > state_;
+};
+
+class Void {};
+
+} /* namespace pulsar */
+
+#endif /* LIB_FUTURE_H_ */
diff --git a/src/utils.h b/src/utils.h
index 4b69ff8..3cbf98a 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -23,7 +23,7 @@
#include <pulsar/Client.h>
#include <pulsar/MessageBatch.h>
-#include <lib/Utils.h>
+#include "future.h"
using namespace pulsar;
@@ -40,6 +40,29 @@ inline void CHECK_RESULT(Result res) {
}
}
+struct WaitForCallback {
+ Promise<bool, Result> m_promise;
+
+ WaitForCallback(Promise<bool, Result> promise) : m_promise(promise) {}
+
+ void operator()(Result result) { m_promise.setValue(result); }
+};
+
+template <typename T>
+struct WaitForCallbackValue {
+ Promise<Result, T>& m_promise;
+
+ WaitForCallbackValue(Promise<Result, T>& promise) : m_promise(promise) {}
+
+ void operator()(Result result, const T& value) {
+ if (result == ResultOk) {
+ m_promise.setValue(value);
+ } else {
+ m_promise.setFailed(result);
+ }
+ }
+};
+
void waitForAsyncResult(std::function<void(ResultCallback)> func);
template <typename T, typename Callback>
diff --git a/version.txt b/version.txt
new file mode 100644
index 0000000..05f2b11
--- /dev/null
+++ b/version.txt
@@ -0,0 +1,5 @@
+
+pulsar-client-python: 3.0.0a1
+
+# Dependency
+pulsar-client-cpp: 2.10.1