Hello community, here is the log from the commit of package python-hiredis for openSUSE:Factory checked in at 2019-08-27 10:27:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-hiredis (Old) and /work/SRC/openSUSE:Factory/.python-hiredis.new.7948 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-hiredis" Tue Aug 27 10:27:21 2019 rev:2 rq:726269 version:1.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-hiredis/python-hiredis.changes 2018-08-31 10:46:44.611369390 +0200 +++ /work/SRC/openSUSE:Factory/.python-hiredis.new.7948/python-hiredis.changes 2019-08-27 10:27:22.971914451 +0200 @@ -1,0 +2,16 @@ +Mon Aug 26 11:43:18 UTC 2019 - Marketa Calabkova <mcalabk...@suse.com> + +- Update to 1.0.0 + * (BREAKING CHANGE) Add ability to control how unicode decoding + errors are handled + * Removed support for EOL Python 2.6, 3.2, and 3.3. + * Upgrade hiredis to 0.13.3 + * Fix non-utf8 reply parsing causing segmentation fault in Python 3 + * Expose len method to retrieve the buffer length + * Fix crash when custom exception raise error (on init) + * Sort list of source files to allow reproducible building +- Drop obsolete patch reproducible-build.patch +- Added patch drop-vendor-sources.patch + * the vendor directory appears empty + +------------------------------------------------------------------- Old: ---- reproducible-build.patch v0.2.0.tar.gz New: ---- drop-vendor-sources.patch v1.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-hiredis.spec ++++++ --- /var/tmp/diff_new_pack.egE8u9/_old 2019-08-27 10:27:24.375914346 +0200 +++ /var/tmp/diff_new_pack.egE8u9/_new 2019-08-27 10:27:24.379914347 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-hiredis # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,13 +12,13 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-hiredis -Version: 0.2.0 +Version: 1.0.0 Release: 0 Summary: Python wrapper for hiredis License: BSD-3-Clause @@ -26,12 +26,12 @@ URL: https://github.com/redis/hiredis-py Source: https://github.com/redis/hiredis-py/archive/v%{version}.tar.gz Patch0: 0001-Use-system-libhiredis.patch -Patch1: reproducible-build.patch -Patch2: fix_build_dir_in_tests.patch +Patch1: fix_build_dir_in_tests.patch +Patch2: drop-vendor-sources.patch BuildRequires: %{python_module devel} BuildRequires: %{python_module setuptools} BuildRequires: fdupes -BuildRequires: hiredis-devel +BuildRequires: hiredis-devel >= 0.13.3 BuildRequires: python-rpm-macros %python_subpackages ++++++ 0001-Use-system-libhiredis.patch ++++++ --- /var/tmp/diff_new_pack.egE8u9/_old 2019-08-27 10:27:24.391914346 +0200 +++ /var/tmp/diff_new_pack.egE8u9/_new 2019-08-27 10:27:24.391914346 +0200 @@ -12,33 +12,17 @@ setup.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) -diff --git a/setup.py b/setup.py -index 9eab077..ed74eb8 100755 ---- a/setup.py -+++ b/setup.py -@@ -45,7 +45,8 @@ lib = ("hiredis_for_hiredis_py", { - +Index: hiredis-py-1.0.0/setup.py +=================================================================== +--- hiredis-py-1.0.0.orig/setup.py ++++ hiredis-py-1.0.0/setup.py +@@ -13,7 +13,8 @@ def version(): ext = Extension("hiredis.hiredis", - sources=glob.glob("src/*.c"), + sources=sorted(glob.glob("src/*.c") + + ["vendor/hiredis/%s.c" % src for src in ("read", "sds")]), - include_dirs=["vendor"]) + include_dirs=["vendor"], + extra_link_args=["-lhiredis"]) setup( name="hiredis", -@@ -57,11 +58,13 @@ setup( - keywords=["Redis"], - license="BSD", - packages=["hiredis"], -- libraries=[lib], -+ # Disabled in Debian, we use the system hiredis library -+ # libraries=[lib], - ext_modules=[ext], - - # Override "install_lib" command -- cmdclass={ "install_lib": install_lib }, -+ # Debian: disable and link against the system hiredis library -+ # cmdclass={ "install_lib": install_lib }, - - classifiers=[ - 'Development Status :: 5 - Production/Stable', ++++++ drop-vendor-sources.patch ++++++ Index: hiredis-py-1.0.0/setup.py =================================================================== --- hiredis-py-1.0.0.orig/setup.py +++ hiredis-py-1.0.0/setup.py @@ -11,8 +11,7 @@ def version(): return module.__version__ ext = Extension("hiredis.hiredis", - sources=sorted(glob.glob("src/*.c") + - ["vendor/hiredis/%s.c" % src for src in ("read", "sds")]), + sources=sorted(glob.glob("src/*.c")), include_dirs=["vendor"], extra_link_args=["-lhiredis"]) ++++++ v0.2.0.tar.gz -> v1.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/.travis.yml new/hiredis-py-1.0.0/.travis.yml --- old/hiredis-py-0.2.0/.travis.yml 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/.travis.yml 2019-01-20 08:39:27.000000000 +0100 @@ -1,12 +1,83 @@ +sudo: false language: python +dist: trusty -python: - - "2.6" - - "2.7" - - "3.2" - - "3.3" - - "3.4" -install: "python setup.py build" +matrix: + include: + - python: "2.7" + - python: "3.4" + - python: "3.5" + - python: "3.6" + - python: "3.7" + dist: xenial + sudo: required + # linux wheels + - dist: trusty + sudo: required + services: + - docker + env: HIREDIS_PY_BUILDWHEELS=1 + # osx wheels + - os: osx + osx_image: xcode9.4 + language: generic + env: HIREDIS_PY_BUILDWHEELS=1 + python: "3.6" + before_install: + - SSL_CERT_FILE=$(brew --prefix)/etc/openssl/cert.pem + - sudo pip install -U pip setuptools twine -script: "python test.py" +branches: + only: + - staging + - trying + - master + - /^v.*$/ + +install: + - | + if [ -n "${HIREDIS_PY_BUILDWHEELS:-}" ]; then + pip install cibuildwheel + else + python setup.py build + fi + +script: + - | + if [ -n "${HIREDIS_PY_BUILDWHEELS:-}" ]; then + cibuildwheel --output-dir dist + else + python test.py + fi + +before_deploy: ls dist/ + +deploy: + # deploy master non-tags to Test PyPI + - provider: pypi + user: ifduyue + password: + secure: "Hn8n7k11TQQF2PWbx8aYhVf6j5bVh8s9/5HuA0eEW4Vl3TmBzyrh2OPKPgrlh9WLvkBUyl0SJzvqxh+SOKP3dg6XOItvZzm/ZnN77gVbrMkjpNOmfENb6Amdx7y1uDG60UFd5H35D8SoinGmW9QSyxMjB7eIH+qybGUXoSV4BaM=" + server: https://test.pypi.org/legacy/ + distributions: sdist + skip_existing: true + skip_cleanup: true + on: + repo: redis/hiredis-py + tags: false + branch: master + condition: -n "${HIREDIS_PY_BUILDWHEELS:-}" + + # deploy tags to PyPI + - provider: pypi + user: ifduyue + password: + secure: "WgO8677gsCeftEIdozL5albCmXuVwuyHLZur6mP1cvEGDGdzatDCwZJkM1pdOCy4xXYYz3+bMsya5gLbQmGZOYBzieAb4CYR+O38Kd0mVCoZpK7TYmN55G+Tn3bztxFOBtInqd9bf1JkPE5eXN7Lc4rkMhMmafxoN8aBVPlfhRM=" + distributions: sdist + skip_existing: true + skip_cleanup: true + on: + repo: redis/hiredis-py + tags: true + condition: -n "${HIREDIS_PY_BUILDWHEELS:-}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/CHANGELOG.md new/hiredis-py-1.0.0/CHANGELOG.md --- old/hiredis-py-0.2.0/CHANGELOG.md 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/CHANGELOG.md 2019-01-20 08:39:27.000000000 +0100 @@ -1,3 +1,24 @@ +### 1.0.0 (2019-01-20) + +* **(BREAKING CHANGE)** Add ability to control how unicode decoding errors are handled (see #82) +* Removed support for EOL Python 2.6, 3.2, and 3.3. + +### 0.3.1 (2018-12-24) + +* Include test files in sdist tarball (see #80) + +### 0.3.0 (2018-11-16) + +* Upgrade hiredis to 0.13.3 +* Add optional "shouldDecode" argument to Reader.gets() (see #77) +* Add a "has_data" method to Reader objects (see #78) +* Fix non-utf8 reply parsing causing segmentation fault in Python 3 (see #73) +* Rename `state` to `hiredis_py_module_state` to avoid conflicts (see #72) +* Expose len method to retrieve the buffer length (see #61) +* Fix crash when custom exception raise error (on init) (see #57) +* incref before PyModule_AddObject which steals references (see #48) +* Sort list of source files to allow reproducible building (see #47) + ### 0.2.0 (2015-04-03) * Allow usage of setuptools diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/MANIFEST.in new/hiredis-py-1.0.0/MANIFEST.in --- old/hiredis-py-0.2.0/MANIFEST.in 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/MANIFEST.in 2019-01-20 08:39:27.000000000 +0100 @@ -1,7 +1,10 @@ include COPYING include MANIFEST.in +include README.md include src/*.[ch] include vendor/hiredis/COPYING include vendor/hiredis/*.[ch] -exclude vendor/hiredis/example* -exclude vendor/hiredis/text* +include test.py +graft test +global-exclude __pycache__ +global-exclude *.py[co] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/README.md new/hiredis-py-1.0.0/README.md --- old/hiredis-py-0.2.0/README.md 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/README.md 2019-01-20 08:39:27.000000000 +0100 @@ -1,6 +1,7 @@ # hiredis-py -[![Build Status](https://travis-ci.org/redis/hiredis-py.svg?branch=v0.1.2)](https://travis-ci.org/redis/hiredis-py) +[![Build Status](https://travis-ci.org/redis/hiredis-py.svg?branch=master)](https://travis-ci.org/redis/hiredis-py) +[![Windows Build Status](https://ci.appveyor.com/api/projects/status/muso9gbe316tjsac/branch/master?svg=true)](https://ci.appveyor.com/project/duyue/hiredis-py/) Python extension that wraps protocol parsing code in [hiredis][hiredis]. It primarily speeds up parsing of multi bulk replies. @@ -9,19 +10,20 @@ ## Install -hiredis-py is available on [PyPi](http://pypi.python.org/pypi/hiredis), and -can be installed with: +hiredis-py is available on [PyPI](https://pypi.org/project/hiredis/), and can +be installed with: ``` -easy_install hiredis +pip install hiredis ``` ### Requirements -hiredis-py requires **Python 2.6 or higher**. +hiredis-py requires **Python 2.7 or 3.4+**. Make sure Python development headers are available when installing hiredis-py. -On Ubuntu/Debian systems, install them with `apt-get install python-dev`. +On Ubuntu/Debian systems, install them with `apt-get install python-dev` for Python 2 +or `apt-get install python3-dev` for Python 3. ## Usage @@ -66,16 +68,23 @@ initializing it: ```python ->>> reader = hiredis.Reader(encoding="utf-8") +>>> reader = hiredis.Reader(encoding="utf-8", errors="strict") >>> reader.feed("$3\r\n\xe2\x98\x83\r\n") >>> reader.gets() u'☃' ``` -When bulk data in a reply could not be properly decoded using the specified -encoding, it will be returned as a plain string. When the encoding cannot be -found, a `LookupError` will be raised after calling `gets` for the first reply -with bulk data (identical to what Python's `unicode` method would do). +Decoding of bulk data will be attempted using the specified encoding and +error handler. If the error handler is `'strict'` (the default), a +`UnicodeDecodeError` is raised when data cannot be dedcoded. This is identical +to Python's default behavior. Other valid values to `errors` include +`'replace'`, `'ignore'`, and `'backslashreplace'`. More information on the +behavior of these error handlers can be found +[here](https://docs.python.org/3/howto/unicode.html#the-string-type). + + +When the specified encoding cannot be found, a `LookupError` will be raised +when calling `gets` for the first reply with bulk data. #### Error handling diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/appveyor/run_with_env.cmd new/hiredis-py-1.0.0/appveyor/run_with_env.cmd --- old/hiredis-py-0.2.0/appveyor/run_with_env.cmd 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/appveyor/run_with_env.cmd 2019-01-20 08:39:27.000000000 +0100 @@ -1,4 +1,7 @@ -:: To build extensions for 64 bit Python 3, we need to configure environment +:: To build extensions for 64 bit Python 3.5 or later no special environment needs +:: to be configured. +:: +:: To build extensions for 64 bit Python 3.4 or earlier, we need to configure environment :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) :: @@ -15,31 +18,47 @@ :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows :: http://stackoverflow.com/a/13751649/163740 :: -:: Author: Olivier Grisel +:: Original Author: Olivier Grisel :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ +:: This version based on updates for python 3.5 by Phil Elson at: +:: https://github.com/pelson/Obvious-CI/tree/master/scripts + @ECHO OFF SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows SET MAJOR_PYTHON_VERSION="%PYTHON_VERSION:~0,1%" +SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% IF %MAJOR_PYTHON_VERSION% == "2" ( SET WINDOWS_SDK_VERSION="v7.0" + SET SET_SDK_64=Y ) ELSE IF %MAJOR_PYTHON_VERSION% == "3" ( SET WINDOWS_SDK_VERSION="v7.1" + IF %MINOR_PYTHON_VERSION% LEQ 4 ( + SET SET_SDK_64=Y + ) ELSE ( + SET SET_SDK_64=N + ) ) ELSE ( ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" EXIT 1 ) IF "%PYTHON_ARCH%"=="64" ( - ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture - SET DISTUTILS_USE_SDK=1 - SET MSSdk=1 - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 + IF %SET_SDK_64% == Y ( + ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture + SET DISTUTILS_USE_SDK=1 + SET MSSdk=1 + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 + ) ELSE ( + ECHO Using default MSVC build environment for 64 bit architecture + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 + ) ) ELSE ( ECHO Using default MSVC build environment for 32 bit architecture ECHO Executing: %COMMAND_TO_RUN% diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/appveyor.yml new/hiredis-py-1.0.0/appveyor.yml --- old/hiredis-py-0.2.0/appveyor.yml 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/appveyor.yml 2019-01-20 08:39:27.000000000 +0100 @@ -5,6 +5,11 @@ # /E:ON and /V:ON options are not enabled in the batch script intepreter # See: http://stackoverflow.com/a/13751649/163740 CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd" + PYPI_USER: ifduyue + PYPI_TEST_PASSWORD: + secure: Ub5TGKonq/xFgzRLFMCcKQ== + PYPI_PASSWORD: + secure: fFfFN5N5920gtX3+pwrOddk/psDk3wK67snCOt209bc= matrix: - PYTHON: "C:\\Python27" @@ -15,14 +20,6 @@ PYTHON_VERSION: "2.7.8" PYTHON_ARCH: "64" - - PYTHON: "C:\\Python33" - PYTHON_VERSION: "3.3.5" - PYTHON_ARCH: "32" - - - PYTHON: "C:\\Python33-x64" - PYTHON_VERSION: "3.3.5" - PYTHON_ARCH: "64" - - PYTHON: "C:\\Python34" PYTHON_VERSION: "3.4.1" PYTHON_ARCH: "32" @@ -31,6 +28,37 @@ PYTHON_VERSION: "3.4.1" PYTHON_ARCH: "64" + - PYTHON: "C:\\Python35" + PYTHON_VERSION: "3.5.0" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python35-x64" + PYTHON_VERSION: "3.5.0" + PYTHON_ARCH: "64" + + - PYTHON: "C:\\Python36" + PYTHON_VERSION: "3.6.6" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python36-x64" + PYTHON_VERSION: "3.6.6" + PYTHON_ARCH: "64" + + - PYTHON: "C:\\Python37" + PYTHON_VERSION: "3.7.0" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python37-x64" + PYTHON_VERSION: "3.7.0" + PYTHON_ARCH: "64" + + # build wheels + - PYTHON: "C:\\Python36-x64" + PYTHON_VERSION: "3.6.6" + PYTHON_ARCH: "64" + HIREDIS_PY_BUILDWHEELS: 1 + + install: - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - "python --version" @@ -42,3 +70,32 @@ test_script: - "%CMD_IN_ENV% python setup.py build" - "%CMD_IN_ENV% python test.py" + - ps: | + if (Test-Path env:HIREDIS_PY_BUILDWHEELS) { + python -m pip install -U pip setuptools cibuildwheel + cibuildwheel --output-dir wheels + ls wheels + } + +artifacts: + - path: wheels\*.whl + name: Wheels + +on_success: + # deploy master non-tags to Test PyPI + - ps: | + if (!(Test-Path env:HIREDIS_PY_BUILDWHEELS)) { return } + if (Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) { return } + if ($env:APPVEYOR_REPO_NAME -ne 'redis/hiredis-py') { return } + if ($env:APPVEYOR_REPO_BRANCH -ne 'master') { return } + pip install -U twine + twine upload -u $env:PYPI_USER -p $env:PYPI_TEST_PASSWORD --repository-url https://test.pypi.org/legacy/ --skip-existing wheels\*.whl + + # deploy tags to PyPI + - ps: | + if (!(Test-Path env:HIREDIS_PY_BUILDWHEELS)) { return } + if (Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) { return } + if ($env:APPVEYOR_REPO_NAME -ne 'redis/hiredis-py') { return } + if ($env:APPVEYOR_REPO_TAG -ne 'true') { return } + pip install -U twine + twine upload -u $env:PYPI_USER -p $env:PYPI_PASSWORD --skip-existing wheels\*.whl diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/hiredis/version.py new/hiredis-py-1.0.0/hiredis/version.py --- old/hiredis-py-0.2.0/hiredis/version.py 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/hiredis/version.py 2019-01-20 08:39:27.000000000 +0100 @@ -1 +1 @@ -__version__ = "0.2.0" +__version__ = "1.0.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/setup.py new/hiredis-py-1.0.0/setup.py --- old/hiredis-py-0.2.0/setup.py 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/setup.py 2019-01-20 08:39:27.000000000 +0100 @@ -2,67 +2,33 @@ try: from setuptools import setup, Extension - from setuptools.command import install_lib as _install_lib except ImportError: from distutils.core import setup, Extension - from distutils.command import install_lib as _install_lib import sys, imp, os, glob def version(): module = imp.load_source("hiredis.version", "hiredis/version.py") return module.__version__ -# Patch "install_lib" command to run build_clib before build_ext -# to properly work with easy_install. -# See: http://bugs.python.org/issue5243 -class install_lib(_install_lib.install_lib): - def build(self): - if not self.skip_build: - if self.distribution.has_pure_modules(): - self.run_command('build_py') - if self.distribution.has_c_libraries(): - self.run_command('build_clib') - if self.distribution.has_ext_modules(): - self.run_command('build_ext') - -# To link the extension with the C library, distutils passes the "-lLIBRARY" -# option to the linker. This makes it go through its library search path. If it -# finds a shared object of the specified library in one of the system-wide -# library paths, it will dynamically link it. -# -# We want the linker to statically link the version of hiredis that is included -# with hiredis-py. However, the linker may pick up the shared library version -# of hiredis, if it is available through one of the system-wide library paths. -# To prevent this from happening, we use an obfuscated library name such that -# the only version the linker will be able to find is the right version. -# -# This is a terrible hack, but patching distutils to do the right thing for all -# supported Python versions is worse... -# -# Also see: https://github.com/pietern/hiredis-py/issues/15 -lib = ("hiredis_for_hiredis_py", { - "sources": ["vendor/hiredis/%s.c" % src for src in ("read", "sds")]}) - ext = Extension("hiredis.hiredis", - sources=glob.glob("src/*.c"), + sources=sorted(glob.glob("src/*.c") + + ["vendor/hiredis/%s.c" % src for src in ("read", "sds")]), include_dirs=["vendor"]) setup( name="hiredis", version=version(), description="Python wrapper for hiredis", + long_description=open('README.md', 'r').read(), + long_description_content_type='text/markdown', url="https://github.com/redis/hiredis-py", author="Jan-Erik Rediger, Pieter Noordhuis", author_email="jane...@fnordig.de, pcnoordh...@gmail.com", keywords=["Redis"], license="BSD", packages=["hiredis"], - libraries=[lib], ext_modules=[ext], - - # Override "install_lib" command - cmdclass={ "install_lib": install_lib }, - + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', @@ -71,12 +37,12 @@ 'Operating System :: POSIX', 'Programming Language :: C', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Software Development', ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/src/hiredis.c new/hiredis-py-1.0.0/src/hiredis.c --- old/hiredis-py-0.2.0/src/hiredis.c 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/src/hiredis.c 2019-01-20 08:39:27.000000000 +0100 @@ -28,7 +28,7 @@ NULL /* m_free */ }; #else -struct hiredis_ModuleState state; +struct hiredis_ModuleState hiredis_py_module_state; #endif /* Keep pointer around for other classes to access the module state. */ @@ -63,8 +63,11 @@ HIREDIS_STATE->HiErr_ReplyError = PyErr_NewException(MOD_HIREDIS ".ReplyError", HIREDIS_STATE->HiErr_Base, NULL); + Py_INCREF(HIREDIS_STATE->HiErr_Base); PyModule_AddObject(mod_hiredis, "HiredisError", HIREDIS_STATE->HiErr_Base); + Py_INCREF(HIREDIS_STATE->HiErr_ProtocolError); PyModule_AddObject(mod_hiredis, "ProtocolError", HIREDIS_STATE->HiErr_ProtocolError); + Py_INCREF(HIREDIS_STATE->HiErr_ReplyError); PyModule_AddObject(mod_hiredis, "ReplyError", HIREDIS_STATE->HiErr_ReplyError); Py_INCREF(&hiredis_ReaderType); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/src/hiredis.h new/hiredis-py-1.0.0/src/hiredis.h --- old/hiredis-py-0.2.0/src/hiredis.h 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/src/hiredis.h 2019-01-20 08:39:27.000000000 +0100 @@ -25,8 +25,8 @@ #if IS_PY3K #define GET_STATE(__s) ((struct hiredis_ModuleState*)PyModule_GetState(__s)) #else -extern struct hiredis_ModuleState state; -#define GET_STATE(__s) (&state) +extern struct hiredis_ModuleState hiredis_py_module_state; +#define GET_STATE(__s) (&hiredis_py_module_state) #endif /* Keep pointer around for other classes to access the module state. */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/src/reader.c new/hiredis-py-1.0.0/src/reader.c --- old/hiredis-py-0.2.0/src/reader.c 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/src/reader.c 2019-01-20 08:39:27.000000000 +0100 @@ -6,15 +6,19 @@ static int Reader_init(hiredis_ReaderObject *self, PyObject *args, PyObject *kwds); static PyObject *Reader_new(PyTypeObject *type, PyObject *args, PyObject *kwds); static PyObject *Reader_feed(hiredis_ReaderObject *self, PyObject *args); -static PyObject *Reader_gets(hiredis_ReaderObject *self); +static PyObject *Reader_gets(hiredis_ReaderObject *self, PyObject *args); static PyObject *Reader_setmaxbuf(hiredis_ReaderObject *self, PyObject *arg); static PyObject *Reader_getmaxbuf(hiredis_ReaderObject *self); +static PyObject *Reader_len(hiredis_ReaderObject *self); +static PyObject *Reader_has_data(hiredis_ReaderObject *self); static PyMethodDef hiredis_ReaderMethods[] = { {"feed", (PyCFunction)Reader_feed, METH_VARARGS, NULL }, - {"gets", (PyCFunction)Reader_gets, METH_NOARGS, NULL }, + {"gets", (PyCFunction)Reader_gets, METH_VARARGS, NULL }, {"setmaxbuf", (PyCFunction)Reader_setmaxbuf, METH_O, NULL }, {"getmaxbuf", (PyCFunction)Reader_getmaxbuf, METH_NOARGS, NULL }, + {"len", (PyCFunction)Reader_len, METH_NOARGS, NULL }, + {"has_data", (PyCFunction)Reader_has_data, METH_NOARGS, NULL }, { NULL } /* Sentinel */ }; @@ -72,28 +76,20 @@ static PyObject *createDecodedString(hiredis_ReaderObject *self, const char *str, size_t len) { PyObject *obj; - if (self->encoding == NULL) { + if (self->encoding == NULL || !self->shouldDecode) { obj = PyBytes_FromStringAndSize(str, len); } else { - obj = PyUnicode_Decode(str, len, self->encoding, NULL); + obj = PyUnicode_Decode(str, len, self->encoding, self->errors); if (obj == NULL) { - if (PyErr_ExceptionMatches(PyExc_ValueError)) { - /* Ignore encoding and simply return plain string. */ - obj = PyBytes_FromStringAndSize(str, len); - } else { - assert(PyErr_ExceptionMatches(PyExc_LookupError)); - - /* Store error when this is the first. */ - if (self->error.ptype == NULL) - PyErr_Fetch(&(self->error.ptype), &(self->error.pvalue), - &(self->error.ptraceback)); - - /* Return Py_None as placeholder to let the error bubble up and - * be used when a full reply in Reader#gets(). */ - obj = Py_None; - Py_INCREF(obj); - } - + /* Store error when this is the first. */ + if (self->error.ptype == NULL) + PyErr_Fetch(&(self->error.ptype), &(self->error.pvalue), + &(self->error.ptraceback)); + + /* Return Py_None as placeholder to let the error bubble up and + * be used when a full reply in Reader#gets(). */ + obj = Py_None; + Py_INCREF(obj); PyErr_Clear(); } } @@ -103,13 +99,19 @@ } static void *createError(PyObject *errorCallable, char *errstr, size_t len) { - PyObject *obj; + PyObject *obj, *errmsg; + + #if IS_PY3K + errmsg = PyUnicode_DecodeUTF8(errstr, len, "replace"); + #else + errmsg = Py_BuildValue("s#", errstr, len); + #endif + assert(errmsg != NULL); /* TODO: properly handle OOM etc */ + + obj = PyObject_CallFunctionObjArgs(errorCallable, errmsg, NULL); + Py_DECREF(errmsg); + /* obj can be NULL if custom error class raised another exception */ - PyObject *args = Py_BuildValue("(s#)", errstr, len); - assert(args != NULL); /* TODO: properly handle OOM etc */ - obj = PyObject_CallObject(errorCallable, args); - assert(obj != NULL); - Py_DECREF(args); return obj; } @@ -119,10 +121,16 @@ if (task->type == REDIS_REPLY_ERROR) { obj = createError(self->replyErrorClass, str, len); + if (obj == NULL) { + if (self->error.ptype == NULL) + PyErr_Fetch(&(self->error.ptype), &(self->error.pvalue), + &(self->error.ptraceback)); + obj = Py_None; + Py_INCREF(obj); + } } else { obj = createDecodedString(self, str, len); } - return tryParentize(task, obj); } @@ -157,9 +165,9 @@ }; static void Reader_dealloc(hiredis_ReaderObject *self) { + // we don't need to free self->encoding as the buffer is managed by Python + // https://docs.python.org/3/c-api/arg.html#strings-and-buffers redisReplyReaderFree(self->reader); - if (self->encoding) - free(self->encoding); Py_XDECREF(self->protocolErrorClass); Py_XDECREF(self->replyErrorClass); @@ -182,13 +190,14 @@ } static int Reader_init(hiredis_ReaderObject *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = { "protocolError", "replyError", "encoding", NULL }; + static char *kwlist[] = { "protocolError", "replyError", "encoding", "errors", NULL }; PyObject *protocolErrorClass = NULL; PyObject *replyErrorClass = NULL; - PyObject *encodingObj = NULL; + char *encoding = NULL; + char *errors = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, - &protocolErrorClass, &replyErrorClass, &encodingObj)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOss", kwlist, + &protocolErrorClass, &replyErrorClass, &encoding, &errors)) return -1; if (protocolErrorClass) @@ -199,25 +208,19 @@ if (!_Reader_set_exception(&self->replyErrorClass, replyErrorClass)) return -1; - if (encodingObj) { - PyObject *encbytes; - char *encstr; - int enclen; - - if (PyUnicode_Check(encodingObj)) - encbytes = PyUnicode_AsASCIIString(encodingObj); - else - encbytes = PyObject_Bytes(encodingObj); - - if (encbytes == NULL) + self->encoding = encoding; + if(errors) { + if (strcmp(errors, "strict") != 0 && + strcmp(errors, "replace") != 0 && + strcmp(errors, "ignore") != 0 && + strcmp(errors, "backslashreplace") != 0) { + + PyErr_SetString(PyExc_LookupError, + "if specified, errors must be one of " + "{'strict', 'replace', 'ignore', 'backslashreplace'}"); return -1; - - enclen = PyBytes_Size(encbytes); - encstr = PyBytes_AsString(encbytes); - self->encoding = (char*)malloc(enclen+1); - memcpy(self->encoding, encstr, enclen); - self->encoding[enclen] = '\0'; - Py_DECREF(encbytes); + } + self->errors = errors; } return 0; @@ -232,6 +235,8 @@ self->reader->privdata = self; self->encoding = NULL; + self->errors = "strict"; // default to "strict" to mimic Python + self->shouldDecode = 1; self->protocolErrorClass = HIREDIS_STATE->HiErr_ProtocolError; self->replyErrorClass = HIREDIS_STATE->HiErr_ReplyError; Py_INCREF(self->protocolErrorClass); @@ -276,19 +281,26 @@ return NULL; } -static PyObject *Reader_gets(hiredis_ReaderObject *self) { +static PyObject *Reader_gets(hiredis_ReaderObject *self, PyObject *args) { PyObject *obj; PyObject *err; char *errstr; + self->shouldDecode = 1; + if (!PyArg_ParseTuple(args, "|i", &self->shouldDecode)) { + return NULL; + } + if (redisReplyReaderGetReply(self->reader, (void**)&obj) == REDIS_ERR) { errstr = redisReplyReaderGetError(self->reader); /* protocolErrorClass might be a callable. call it, then use it's type */ err = createError(self->protocolErrorClass, errstr, strlen(errstr)); - obj = PyObject_Type(err); - PyErr_SetString(obj, errstr); - Py_DECREF(obj); - Py_DECREF(err); + if (err != NULL) { + obj = PyObject_Type(err); + PyErr_SetString(obj, errstr); + Py_DECREF(obj); + Py_DECREF(err); + } return NULL; } @@ -332,3 +344,13 @@ static PyObject *Reader_getmaxbuf(hiredis_ReaderObject *self) { return PyLong_FromSize_t(self->reader->maxbuf); } + +static PyObject *Reader_len(hiredis_ReaderObject *self) { + return PyLong_FromSize_t(self->reader->len); +} + +static PyObject *Reader_has_data(hiredis_ReaderObject *self) { + if(self->reader->pos < self->reader->len) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/src/reader.h new/hiredis-py-1.0.0/src/reader.h --- old/hiredis-py-0.2.0/src/reader.h 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/src/reader.h 2019-01-20 08:39:27.000000000 +0100 @@ -7,6 +7,8 @@ PyObject_HEAD redisReader *reader; char *encoding; + char *errors; + int shouldDecode; PyObject *protocolErrorClass; PyObject *replyErrorClass; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hiredis-py-0.2.0/test/reader.py new/hiredis-py-1.0.0/test/reader.py --- old/hiredis-py-0.2.0/test/reader.py 2015-04-03 22:50:50.000000000 +0200 +++ new/hiredis-py-1.0.0/test/reader.py 2019-01-20 08:39:27.000000000 +0100 @@ -3,6 +3,8 @@ import hiredis import sys +IS_PY3K = sys.version_info[0] >= 3 + class ReaderTest(TestCase): def setUp(self): self.reader = hiredis.Reader() @@ -36,6 +38,13 @@ def test_fail_with_wrong_protocol_error_class(self): self.assertRaises(TypeError, hiredis.Reader, protocolError="wrong") + def test_faulty_protocol_error_class(self): + def make_error(errstr): + 1 / 0 + self.reader = hiredis.Reader(protocolError=make_error) + self.reader.feed(b"x") + self.assertRaises(ZeroDivisionError, self.reply) + def test_error_string(self): self.reader.feed(b"-error\r\n") error = self.reply() @@ -62,9 +71,29 @@ self.assertEquals(CustomException, type(error)) self.assertEquals(("error",), error.args) + def test_error_string_with_non_utf8_chars(self): + self.reader.feed(b"-error \xd1\r\n") + error = self.reply() + + if IS_PY3K: + expected = "error \ufffd" + else: + expected = b"error \xd1" + + self.assertEquals(hiredis.ReplyError, type(error)) + self.assertEquals((expected,), error.args) + def test_fail_with_wrong_reply_error_class(self): self.assertRaises(TypeError, hiredis.Reader, replyError="wrong") + def test_faulty_reply_error_class(self): + def make_error(errstr): + 1 / 0 + + self.reader = hiredis.Reader(replyError=make_error) + self.reader.feed(b"-error\r\n") + self.assertRaises(ZeroDivisionError, self.reply) + def test_errors_in_nested_multi_bulk(self): self.reader.feed(b"*2\r\n-err0\r\n-err1\r\n") @@ -72,6 +101,18 @@ self.assertEquals(hiredis.ReplyError, type(error)) self.assertEquals((r,), error.args) + def test_errors_with_non_utf8_chars_in_nested_multi_bulk(self): + self.reader.feed(b"*2\r\n-err\xd1\r\n-err1\r\n") + + if IS_PY3K: + expected = "err\ufffd" + else: + expected = b"err\xd1" + + for r, error in zip((expected, "err1"), self.reply()): + self.assertEquals(hiredis.ReplyError, type(error)) + self.assertEquals((r,), error.args) + def test_integer(self): value = 2**63-1 # Largest 64-bit signed integer self.reader.feed((":%d\r\n" % value).encode("ascii")) @@ -100,17 +141,41 @@ self.reader.feed(b"$3\r\n" + snowman + b"\r\n") self.assertEquals(snowman.decode("utf-8"), self.reply()) - def test_bulk_string_with_other_encoding(self): - snowman = b"\xe2\x98\x83" - self.reader = hiredis.Reader(encoding="utf-32") - self.reader.feed(b"$3\r\n" + snowman + b"\r\n") - self.assertEquals(snowman, self.reply()) - def test_bulk_string_with_invalid_encoding(self): self.reader = hiredis.Reader(encoding="unknown") self.reader.feed(b"$5\r\nhello\r\n") self.assertRaises(LookupError, self.reply) + def test_decode_errors_defaults_to_strict(self): + self.reader = hiredis.Reader(encoding="utf-8") + self.reader.feed(b"+\x80\r\n") + self.assertRaises(UnicodeDecodeError, self.reader.gets) + + def test_decode_error_with_ignore_errors(self): + self.reader = hiredis.Reader(encoding="utf-8", errors="ignore") + self.reader.feed(b"+\x80value\r\n") + self.assertEquals("value", self.reader.gets()) + + def test_invalid_encoding_error_handler(self): + self.assertRaises(LookupError, hiredis.Reader, errors='unknown') + + def test_reader_with_invalid_error_handler(self): + self.assertRaises(LookupError, hiredis.Reader, encoding="utf-8", errors='foo') + + def test_should_decode_false_flag_prevents_decoding(self): + snowman = b"\xe2\x98\x83" + self.reader = hiredis.Reader(encoding="utf-8") + self.reader.feed(b"$3\r\n" + snowman + b"\r\n") + self.reader.feed(b"$3\r\n" + snowman + b"\r\n") + self.assertEquals(snowman, self.reader.gets(False)) + self.assertEquals(snowman.decode("utf-8"), self.reply()) + + def test_should_decode_true_flag_decodes_as_normal(self): + snowman = b"\xe2\x98\x83" + self.reader = hiredis.Reader(encoding="utf-8") + self.reader.feed(b"$3\r\n" + snowman + b"\r\n") + self.assertEquals(snowman.decode("utf-8"), self.reader.gets(True)) + def test_null_multi_bulk(self): self.reader.feed(b"*-1\r\n") self.assertEquals(None, self.reply()) @@ -166,9 +231,8 @@ self.assertEquals(b"ok", self.reply()) def test_feed_bytearray(self): - if sys.hexversion >= 0x02060000: - self.reader.feed(bytearray(b"+ok\r\n")) - self.assertEquals(b"ok", self.reply()) + self.reader.feed(bytearray(b"+ok\r\n")) + self.assertEquals(b"ok", self.reply()) def test_maxbuf(self): defaultmaxbuf = self.reader.getmaxbuf() @@ -179,3 +243,26 @@ self.reader.setmaxbuf(None) self.assertEquals(defaultmaxbuf, self.reader.getmaxbuf()) self.assertRaises(ValueError, self.reader.setmaxbuf, -4) + + def test_len(self): + self.assertEquals(0, self.reader.len()) + data = b"+ok\r\n" + self.reader.feed(data) + self.assertEquals(len(data), self.reader.len()) + + # hiredis reallocates and removes unused buffer once + # there is at least 1K of not used data. + calls = int((1024 / len(data))) + 1 + for i in range(calls): + self.reader.feed(data) + self.reply() + + self.assertEquals(5, self.reader.len()) + + def test_reader_has_data(self): + self.assertEquals(False, self.reader.has_data()) + data = b"+ok\r\n" + self.reader.feed(data) + self.assertEquals(True, self.reader.has_data()) + self.reply() + self.assertEquals(False, self.reader.has_data())