Imported Upstream version 7.45.3 upstream upstream/7.45.3
authorJinWang An <jinwang.an@samsung.com>
Thu, 11 Apr 2024 22:31:45 +0000 (07:31 +0900)
committerJinWang An <jinwang.an@samsung.com>
Thu, 11 Apr 2024 22:31:45 +0000 (07:31 +0900)
56 files changed:
.appveyor/install.ps1 [new file with mode: 0644]
.appveyor/run_with_env.cmd [new file with mode: 0644]
.github/CONTRIBUTING.md [new file with mode: 0644]
.github/ISSUE_TEMPLATE.md [new file with mode: 0644]
.github/workflows/ci-macos.yml [new file with mode: 0644]
.github/workflows/ci-windows.yml [new file with mode: 0644]
.github/workflows/ci.yml [new file with mode: 0644]
.github/workflows/cibuildwheel.yml [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.travis.yml [new file with mode: 0644]
AUTHORS
ChangeLog
INSTALL.rst
PKG-INFO [deleted file]
README.kr.rst [new file with mode: 0644]
README.rst
RELEASE-NOTES.rst
appveyor.yml [new file with mode: 0644]
doc/callbacks.rst
doc/conf.py
doc/index.rst
pycurl.egg-info/PKG-INFO [deleted file]
pycurl.egg-info/SOURCES.txt [deleted file]
pycurl.egg-info/dependency_links.txt [deleted file]
pycurl.egg-info/top_level.txt [deleted file]
pyproject.toml [new file with mode: 0644]
requirements-dev.txt
scripts/missing-symbols [new file with mode: 0755]
setup.cfg [deleted file]
setup.py
src/docstrings.c [deleted file]
src/docstrings.h [deleted file]
src/easy.c
src/easyinfo.c
src/easyopt.c
src/easyperform.c
src/module.c
src/multi.c
src/pycurl.h
src/share.c
src/util.c
tests/bin/realpath [new file with mode: 0755]
tests/c/winsockdup.c [new file with mode: 0644]
tests/ext/test-suite.sh
tests/matrix/check-python.py [new file with mode: 0644]
tests/multi_callback_test.py
tests/multi_socket_select_test.py
tests/multi_test.py
tests/option_constants_test.py
tests/setup_test.py
tests/travis/run.sh [new file with mode: 0755]
tests/travis/setup.sh [new file with mode: 0755]
tests/util.py
tests/version_constants_test.py
tests/win/opensocketcrash.py [new file with mode: 0644]
winbuild.py

diff --git a/.appveyor/install.ps1 b/.appveyor/install.ps1
new file mode 100644 (file)
index 0000000..160ba55
--- /dev/null
@@ -0,0 +1,229 @@
+# Sample script to install Python and pip under Windows
+# Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer
+# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
+
+$MINICONDA_URL = "http://repo.continuum.io/miniconda/"
+$BASE_URL = "https://www.python.org/ftp/python/"
+$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
+$GET_PIP_PATH = "C:\get-pip.py"
+
+$PYTHON_PRERELEASE_REGEX = @"
+(?x)
+(?<major>\d+)
+\.
+(?<minor>\d+)
+\.
+(?<micro>\d+)
+(?<prerelease>[a-z]{1,2}\d+)
+"@
+
+
+function Download ($filename, $url) {
+    $webclient = New-Object System.Net.WebClient
+
+    $basedir = $pwd.Path + "\"
+    $filepath = $basedir + $filename
+    if (Test-Path $filename) {
+        Write-Host "Reusing" $filepath
+        return $filepath
+    }
+
+    # Download and retry up to 3 times in case of network transient errors.
+    Write-Host "Downloading" $filename "from" $url
+    $retry_attempts = 2
+    for ($i = 0; $i -lt $retry_attempts; $i++) {
+        try {
+            $webclient.DownloadFile($url, $filepath)
+            break
+        }
+        Catch [Exception]{
+            Start-Sleep 1
+        }
+    }
+    if (Test-Path $filepath) {
+        Write-Host "File saved at" $filepath
+    } else {
+        # Retry once to get the error message if any at the last try
+        $webclient.DownloadFile($url, $filepath)
+    }
+    return $filepath
+}
+
+
+function ParsePythonVersion ($python_version) {
+    if ($python_version -match $PYTHON_PRERELEASE_REGEX) {
+        return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro,
+                $matches.prerelease)
+    }
+    $version_obj = [version]$python_version
+    return ($version_obj.major, $version_obj.minor, $version_obj.build, "")
+}
+
+
+function DownloadPython ($python_version, $platform_suffix) {
+    $major, $minor, $micro, $prerelease = ParsePythonVersion $python_version
+
+    if (($major -le 2 -and $micro -eq 0) `
+        -or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) `
+        ) {
+        $dir = "$major.$minor"
+        $python_version = "$major.$minor$prerelease"
+    } else {
+        $dir = "$major.$minor.$micro"
+    }
+
+    if ($prerelease) {
+        if (($major -le 2) `
+            -or ($major -eq 3 -and $minor -eq 1) `
+            -or ($major -eq 3 -and $minor -eq 2) `
+            -or ($major -eq 3 -and $minor -eq 3) `
+            ) {
+            $dir = "$dir/prev"
+        }
+    }
+
+    if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) {
+        $ext = "msi"
+        if ($platform_suffix) {
+            $platform_suffix = ".$platform_suffix"
+        }
+    } else {
+        $ext = "exe"
+        if ($platform_suffix) {
+            $platform_suffix = "-$platform_suffix"
+        }
+    }
+
+    $filename = "python-$python_version$platform_suffix.$ext"
+    $url = "$BASE_URL$dir/$filename"
+    $filepath = Download $filename $url
+    return $filepath
+}
+
+
+function InstallPython ($python_version, $architecture, $python_home) {
+    Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
+    if (Test-Path $python_home) {
+        Write-Host $python_home "already exists, skipping."
+        return $false
+    }
+    if ($architecture -eq "32") {
+        $platform_suffix = ""
+    } else {
+        $platform_suffix = "amd64"
+    }
+    $installer_path = DownloadPython $python_version $platform_suffix
+    $installer_ext = [System.IO.Path]::GetExtension($installer_path)
+    Write-Host "Installing $installer_path to $python_home"
+    $install_log = $python_home + ".log"
+    if ($installer_ext -eq '.msi') {
+        InstallPythonMSI $installer_path $python_home $install_log
+    } else {
+        InstallPythonEXE $installer_path $python_home $install_log
+    }
+    if (Test-Path $python_home) {
+        Write-Host "Python $python_version ($architecture) installation complete"
+    } else {
+        Write-Host "Failed to install Python in $python_home"
+        Get-Content -Path $install_log
+        Exit 1
+    }
+}
+
+
+function InstallPythonEXE ($exepath, $python_home, $install_log) {
+    $install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home"
+    RunCommand $exepath $install_args
+}
+
+
+function InstallPythonMSI ($msipath, $python_home, $install_log) {
+    $install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home"
+    $uninstall_args = "/qn /x $msipath"
+    RunCommand "msiexec.exe" $install_args
+    if (-not(Test-Path $python_home)) {
+        Write-Host "Python seems to be installed else-where, reinstalling."
+        RunCommand "msiexec.exe" $uninstall_args
+        RunCommand "msiexec.exe" $install_args
+    }
+}
+
+function RunCommand ($command, $command_args) {
+    Write-Host $command $command_args
+    Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru
+}
+
+
+function InstallPip ($python_home) {
+    $pip_path = $python_home + "\Scripts\pip.exe"
+    $python_path = $python_home + "\python.exe"
+    if (-not(Test-Path $pip_path)) {
+        Write-Host "Installing pip..."
+        $webclient = New-Object System.Net.WebClient
+        $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH)
+        Write-Host "Executing:" $python_path $GET_PIP_PATH
+        & $python_path $GET_PIP_PATH
+    } else {
+        Write-Host "pip already installed."
+    }
+}
+
+
+function DownloadMiniconda ($python_version, $platform_suffix) {
+    if ($python_version -eq "3.4") {
+        $filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe"
+    } else {
+        $filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe"
+    }
+    $url = $MINICONDA_URL + $filename
+    $filepath = Download $filename $url
+    return $filepath
+}
+
+
+function InstallMiniconda ($python_version, $architecture, $python_home) {
+    Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
+    if (Test-Path $python_home) {
+        Write-Host $python_home "already exists, skipping."
+        return $false
+    }
+    if ($architecture -eq "32") {
+        $platform_suffix = "x86"
+    } else {
+        $platform_suffix = "x86_64"
+    }
+    $filepath = DownloadMiniconda $python_version $platform_suffix
+    Write-Host "Installing" $filepath "to" $python_home
+    $install_log = $python_home + ".log"
+    $args = "/S /D=$python_home"
+    Write-Host $filepath $args
+    Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru
+    if (Test-Path $python_home) {
+        Write-Host "Python $python_version ($architecture) installation complete"
+    } else {
+        Write-Host "Failed to install Python in $python_home"
+        Get-Content -Path $install_log
+        Exit 1
+    }
+}
+
+
+function InstallMinicondaPip ($python_home) {
+    $pip_path = $python_home + "\Scripts\pip.exe"
+    $conda_path = $python_home + "\Scripts\conda.exe"
+    if (-not(Test-Path $pip_path)) {
+        Write-Host "Installing pip..."
+        $args = "install --yes pip"
+        Write-Host $conda_path $args
+        Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru
+    } else {
+        Write-Host "pip already installed."
+    }
+}
+
+function main () {
+    InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON
+    InstallPip $env:PYTHON
+}
+
+main
diff --git a/.appveyor/run_with_env.cmd b/.appveyor/run_with_env.cmd
new file mode 100644 (file)
index 0000000..9e2bc85
--- /dev/null
@@ -0,0 +1,104 @@
+:: To build extensions for 64 bit Python 3, 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)
+::
+:: To build extensions for 64 bit Python 2, we need to configure environment
+:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of:
+:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0)
+::
+:: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific
+:: environment configurations.
+::
+:: Note: this script needs to be run with the /E:ON and /V:ON flags for the
+:: cmd interpreter, at least for (SDK v7.0)
+::
+:: More details at:
+:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows
+:: http://stackoverflow.com/a/13751649/163740
+::
+:: Author: Olivier Grisel
+:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
+::
+:: Notes about batch files for Python people:
+::
+:: Quotes in values are literally part of the values:
+::      SET FOO="bar"
+:: FOO is now five characters long: " b a r "
+:: If you don't want quotes, don't include them on the right-hand side.
+::
+:: The CALL lines at the end of this file look redundant, but if you move them
+:: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y
+:: case, I don't know why.
+@ECHO OFF
+
+if %python_version% == 2.7.x (
+    if %python_arch% == 32 (
+        call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86
+    ) else (
+        call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64
+        call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86_amd64
+    )
+) else (
+    if %python_arch% == 32 (
+        call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
+    ) else (
+        call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64
+        call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64
+    )
+)
+
+SET COMMAND_TO_RUN=%*
+SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows
+SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf
+
+:: Extract the major and minor versions, and allow for the minor version to be
+:: more than 9.  This requires the version number to have two dots in it.
+SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1%
+IF "%PYTHON_VERSION:~3,1%" == "." (
+    SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1%
+) ELSE (
+    SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2%
+)
+
+:: Based on the Python version, determine what SDK version to use, and whether
+:: to set the SDK for 64-bit.
+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
+            IF EXIST "%WIN_WDK%" (
+                :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/
+                REN "%WIN_WDK%" 0wdf
+            )
+        )
+    ) ELSE (
+        ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%"
+        EXIT 1
+    )
+)
+
+IF %PYTHON_ARCH% == 64 (
+    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%
+    call %COMMAND_TO_RUN% || EXIT 1
+)
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..664f3e4
--- /dev/null
@@ -0,0 +1,31 @@
+# Contributing to PycURL
+
+## Issues
+
+Please take a moment to consider whether your issue is a bug report or a
+support request.
+
+A bug report should contain code to reproduce the bug, patch to fix the bug,
+or at the very least a pointer to some code in PycURL that you suspect to be
+the problem. If you don't have any of these, your issue is likely a support
+request.
+
+Please send support requests to our mailing list:
+
+https://lists.haxx.se/listinfo/curl-and-python
+
+People have also had success with getting help at Stack Overflow.
+
+If you are not sure whether your issue is a support request or a bug report,
+please post it to the mailing list.
+
+## Pull Requests
+
+Thanks for writing a patch!
+
+PycURL supports many Python versions, libcurl versions and SSL backends.
+When you submit a pull request, Travis CI will run PycURL test suite
+against it on a bunch of different configurations. Please check back after
+10-15 minutes to see if the tests passed. A message will be shown in
+the pull request as to whether the build and test suite succeeded with your
+patch applied.
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644 (file)
index 0000000..3ae2170
--- /dev/null
@@ -0,0 +1,48 @@
+Prior to creating an issue, please review the troubleshooting documentation:
+http://pycurl.io/docs/dev/troubleshooting.html
+
+
+## What did you try to do?
+
+
+## What happened?
+
+If reporting a problem with PycURL installation, include exact commands
+executed as well as complete output.
+
+If reporting a problem with using PycURL, include a SHORT code snippet
+which includes setting `pycurl.VERBOSE` to 1 and reproduces the problem
+and its complete output.
+
+
+## What did you expect to happen?
+
+
+## What is the PycURL version?
+
+(output of `pycurl.version`)
+
+
+## What is your Python version?
+
+(output of `python -V`)
+
+
+## What is your operating system and its version?
+
+
+## Other versions
+
+If your report references any software besides PycURL, for example pip,
+what are the versions of this software? (`pip --version`, etc.)
+
+
+## Is this the most recent PycURL release?
+
+(if not, please check most recent PycURL release before opening the issue)
+
+
+## Did you check libcurl behavior?
+
+(if possible, did you use `curl` command-line tool to verify that the behavior
+you are experiencing is specific to PycURL?)
diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml
new file mode 100644 (file)
index 0000000..4a8384b
--- /dev/null
@@ -0,0 +1,47 @@
+name: CI-macOS
+
+on:
+  push:
+    branches: [ master ]
+    paths-ignore:
+    - '.github/workflows/ci.yml'
+    - '.github/workflows/ci-windows.yml'
+    - '.github/workflows/cibuildwheel.yml'
+  pull_request:
+    branches: [ master ]
+    paths-ignore:
+    - '.github/workflows/ci.yml'
+    - '.github/workflows/ci-windows.yml'
+    - '.github/workflows/cibuildwheel.yml'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  build:
+
+    runs-on: macOS-12
+    env:
+      PYCURL_CURL_CONFIG: /usr/bin/curl-config
+      PYCURL_SSL_LIBRARY: sectransp
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install flake8 pytest
+        if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
+    - name: Build
+      run: make
+    - name: Test with pytest
+      run: make test
diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml
new file mode 100644 (file)
index 0000000..37960f0
--- /dev/null
@@ -0,0 +1,82 @@
+name: CI-Windows
+
+on:
+  push:
+    branches: [ master ]
+    paths-ignore:
+    - '.github/workflows/ci.yml'
+    - '.github/workflows/ci-macos.yml'
+    - '.github/workflows/cibuildwheel.yml'
+  pull_request:
+    branches: [ master ]
+    paths-ignore:
+    - '.github/workflows/ci.yml'
+    - '.github/workflows/ci-macos.yml'
+    - '.github/workflows/cibuildwheel.yml'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  build:
+
+    runs-on: windows-2022
+    env:
+      VCPKG_BINARY_SOURCES: 'clear;nuget,GitHub,readwrite'
+    strategy:
+      matrix:
+        arch: ["x64"]
+        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+      fail-fast: false
+    permissions:
+      packages: write
+
+    steps:
+    - uses: actions/checkout@v3
+    - uses: ilammy/msvc-dev-cmd@v1
+      with:
+        arch: ${{ matrix.arch }}
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+        architecture: ${{ matrix.arch }}
+    - name: 'Setup NuGet credentials'
+      shell: 'bash'
+      run: |
+        nuget="$(vcpkg fetch nuget | tail -n 1)"
+        "${nuget}" \
+          sources add \
+          -source "https://nuget.pkg.github.com/${GITHUB_REPOSITORY_OWNER}/index.json" \
+          -storepasswordincleartext \
+          -name "GitHub" \
+          -username "${GITHUB_REPOSITORY_OWNER}" \
+          -password "${{ secrets.GITHUB_TOKEN }}"
+        "${nuget}" \
+          setapikey "${{ secrets.GITHUB_TOKEN }}" \
+          -source "https://nuget.pkg.github.com/${GITHUB_REPOSITORY_OWNER}/index.json"
+    - name: Install packages
+      run: vcpkg install curl[core,non-http,schannel]:${{ matrix.arch }}-windows
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install wheel delvewheel
+        pip install flake8 pytest -r requirements-dev.txt
+    - name: Build
+      env:
+        PYCURL_SSL_LIBRARY: schannel
+        PYCURL_CURL_DIR: 'C:/vcpkg/packages/curl_${{ matrix.arch }}-windows'
+      run: |
+        python setup.py bdist_wheel
+    - name: Repair & install built wheel
+      run: |
+        delvewheel repair --add-path $VCPKG_INSTALLATION_ROOT/installed/${{ matrix.arch }}-windows/bin dist/*.whl
+        pip install wheelhouse/*.whl
+      shell: bash
+    - name: Test with pytest
+      run: pytest -v -rs
+    - name: Upload wheel
+      uses: actions/upload-artifact@v3
+      with:
+        path: wheelhouse/*.whl
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644 (file)
index 0000000..82202df
--- /dev/null
@@ -0,0 +1,54 @@
+name: CI-Linux
+
+on:
+  push:
+    branches: [ master ]
+    paths-ignore:
+    - '.github/workflows/ci-macos.yml'
+    - '.github/workflows/ci-windows.yml'
+    - '.github/workflows/cibuildwheel.yml'
+  pull_request:
+    branches: [ master ]
+    paths-ignore:
+    - '.github/workflows/ci-macos.yml'
+    - '.github/workflows/ci-windows.yml'
+    - '.github/workflows/cibuildwheel.yml'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  build:
+
+    runs-on: ubuntu-22.04
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install packages
+      run: |
+        sudo apt-get update
+        sudo apt-get install libcurl4-gnutls-dev libgnutls28-dev
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install flake8 pytest
+        if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
+    - name: Lint with flake8
+      run: |
+        # stop the build if there are Python syntax errors or undefined names
+        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
+        # exit-zero treats all errors as warnings.
+        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics
+    - name: Build
+      run: make
+    - name: Test with pytest
+      run: make test
diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml
new file mode 100644 (file)
index 0000000..1cc648b
--- /dev/null
@@ -0,0 +1,83 @@
+name: Build Wheels
+
+on:
+  workflow_dispatch:
+    inputs:
+      pypi-env:
+        description: 'PyPI Environment for Deployment'
+        options:
+        - prod
+        - test
+        required: true
+        default: test
+
+jobs:
+  package-wheel:
+    name: Build wheels on ${{ matrix.os }} ${{ matrix.arch }}
+    runs-on: ${{ matrix.os }}
+    env:
+      VCPKG_BINARY_SOURCES: 'clear;nuget,GitHub,readwrite'
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - os: macOS-14
+          - os: ubuntu-22.04
+          - os: windows-2022
+            arch: x86
+          - os: windows-2022
+            arch: x64
+
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
+        with:
+          python-version: 3.11
+
+      - uses: ilammy/msvc-dev-cmd@v1
+        if: runner.os == 'Windows'
+        with:
+          arch: ${{ matrix.arch }}
+
+      - name: 'Setup NuGet credentials'
+        if: runner.os == 'Windows'
+        shell: 'bash'
+        run: |
+          nuget="$(vcpkg fetch nuget | tail -n 1)"
+          "${nuget}" \
+            sources add \
+            -source "https://nuget.pkg.github.com/${GITHUB_REPOSITORY_OWNER}/index.json" \
+            -storepasswordincleartext \
+            -name "GitHub" \
+            -username "${GITHUB_REPOSITORY_OWNER}" \
+            -password "${{ secrets.GITHUB_TOKEN }}"
+          "${nuget}" \
+            setapikey "${{ secrets.GITHUB_TOKEN }}" \
+            -source "https://nuget.pkg.github.com/${GITHUB_REPOSITORY_OWNER}/index.json"
+
+      - name: Install packages (Windows)
+        if: runner.os == 'Windows'
+        run: vcpkg install curl[core,non-http,schannel]:${{ matrix.arch }}-windows
+
+      - name: Set up QEMU
+        if: runner.os == 'Linux'
+        uses: docker/setup-qemu-action@v3
+        with:
+          platforms: all
+
+      - name: Install cibuildwheel
+        run: python -m pip install cibuildwheel
+
+      - name: Build wheels
+        env:
+          CIBW_ARCHS_WINDOWS: ${{ matrix.arch == 'x86' && 'x86' || 'AMD64' }}
+          CIBW_ENVIRONMENT_WINDOWS: PYCURL_CURL_DIR=C:/vcpkg/packages/curl_${{ matrix.arch }}-windows PYCURL_SSL_LIBRARY=schannel
+          CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: delvewheel repair --add-path C:/vcpkg/installed/${{ matrix.arch }}-windows/bin -w {dest_dir} {wheel}
+        run: |
+          python -m cibuildwheel --output-dir wheelhouse
+
+      - name: Upload wheels
+        uses: actions/upload-artifact@v4
+        with:
+          path: ./wheelhouse/*.whl
+          name: wheels-${{ matrix.os }}-${{ matrix.arch }}
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..e9ff99e
--- /dev/null
@@ -0,0 +1,15 @@
+*.pyc
+*.pyo
+/MANIFEST
+/build
+/dist
+/src/allpycurl.c
+/src/docstrings.c
+/src/docstrings.h
+/tests/tmp
+/tests/fake-curl/libcurl/*.so
+/www/htdocs/download/*.bz2
+/www/htdocs/download/*.exe
+/www/htdocs/download/*.gz
+/www/upload/*
+pycurl.egg-info
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..71387d7
--- /dev/null
@@ -0,0 +1,113 @@
+sudo: false
+dist: bionic
+language: python
+matrix:
+  include:
+    # all supported Python versions
+    - python: 3.9
+      env:
+        - USECURL=7.68.0-openssl11-gssapi-libssh2
+        - USESSL=openssl
+        - USEOPENSSL=1.1.1d
+        - TESTDOCSEXAMPLES=1
+    - python: 3.9
+      env:
+        - USECURL=7.68.0-openssl10-gssapi-libssh2
+        - USESSL=openssl
+        - USEOPENSSL=1.0.2u
+        - TESTDOCSEXAMPLES=1
+    - python: 3.9
+      env:
+        - USECURL=7.68.0-libressl-gssapi-libssh2
+        - USESSL=libressl
+        - USELIBRESSL=3.0.2
+    - python: 3.9
+      env:
+        - USECURL=7.68.0-gnutls-gssapi-libssh2
+        - USESSL=gnutls
+        - TESTDOCSEXAMPLES=1
+    - python: 3.9
+      env:
+        - USECURL=7.68.0-nss-gssapi-libssh2
+        - USESSL=nss
+        # Examples fail with NSS due to CA certs not being explicitly given
+        #- TESTDOCSEXAMPLES=1
+    - python: 3.8
+      env:
+        - USECURL=7.68.0-openssl11-gssapi-libssh2
+        - USESSL=openssl
+        - USEOPENSSL=1.1.1d
+        - TESTDOCSEXAMPLES=1
+    - python: 3.8
+      env:
+        - USECURL=7.68.0-openssl10-gssapi-libssh2
+        - USESSL=openssl
+        - USEOPENSSL=1.0.2u
+        - TESTDOCSEXAMPLES=1
+    - python: 3.8
+      env:
+        - USECURL=7.68.0-libressl-gssapi-libssh2
+        - USESSL=libressl
+        - USELIBRESSL=3.0.2
+    - python: 3.8
+      env:
+        - USECURL=7.68.0-gnutls-gssapi-libssh2
+        - USESSL=gnutls
+        - TESTDOCSEXAMPLES=1
+    - python: 3.8
+      env:
+        - USECURL=7.68.0-nss-gssapi-libssh2
+        - USESSL=nss
+        # Examples fail with NSS due to CA certs not being explicitly given
+        #- TESTDOCSEXAMPLES=1
+    - python: 3.8
+      env:
+        # this configuration should be without gssapi
+        - USECURL=7.68.0-none
+        - USESSL=none
+    - python: 3.5
+      env:
+        - USECURL=7.68.0-openssl10-gssapi-libssh2
+        - USESSL=openssl
+        - USEOPENSSL=1.0.2u
+        - TESTDOCSEXAMPLES=1
+    - python: 3.6
+      env:
+        - USECURL=7.68.0-openssl11-gssapi-libssh2
+        - USESSL=openssl
+        - USEOPENSSL=1.1.1d
+        - TESTDOCSEXAMPLES=1
+    - python: 3.6
+      env:
+        - USECURL=7.68.0-gnutls-gssapi-libssh2
+        - USESSL=gnutls
+        - TESTDOCSEXAMPLES=1
+    - python: 3.6
+      env:
+        - USECURL=7.68.0-nss-gssapi-libssh2
+        - USESSL=nss
+        # Examples fail with NSS due to CA certs not being explicitly given
+        #- TESTDOCSEXAMPLES=1
+
+    # minimum libcurl we support
+    - python: 3.8
+      env:
+        - USECURL=7.19.0-openssl-gssapi
+        - USESSL=openssl
+        # Quickstart does not run on this curl because it is not built with
+        # SSL support.
+        #- TESTDOCSEXAMPLES=1
+    
+    # libcurl development head with various SSL backends
+    - python: 3.8
+      env:
+        - USECURL=dev-openssl11-gssapi-libssh2
+        - USEOPENSSL=1.1.1d
+        - USESSL=openssl
+install: >
+  ./tests/travis/setup.sh
+script: >
+  ./tests/travis/run.sh
+addons:
+  apt:
+    packages: [libssh2-1, libgnutls28-dev]
diff --git a/AUTHORS b/AUTHORS
index 10893e15d8f43594a2730cd9f28cbee8ea33c319..542255cca114b2e5ac432d8e42cfcf543a930b7a 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -34,6 +34,7 @@ decitre <decitre at gmail.com>
 Dima Tisnek <dimaqq at gmail.com>
 Dmitriy Taychenachev <dmitriy.taychenachev at skypicker.com>
 Dmitry Ketov <dketov at gmail.com>
+Dom Sekotill <dom.sekotill at kodo.org.uk>
 Domenico Andreoli <cavok at libero.it>
 Dominique <curl-and-python at d242.net>
 Eneas U de Queiroz <cotequeiroz at gmail.com>
@@ -53,6 +54,7 @@ James Deucker <bitwisecook at users.noreply.github.com>
 Jan Kryl <jan.kryl at nexenta.com>
 Jayne <corvine at gmail.com>
 James Deucker <bitwisecook at users.noreply.github.com>
+Jean Hominal <jhominal at gmail.com>
 JiCiT <jason at infinitebubble.com>
 Jim Patterson
 Josef Schlehofer <pepe.schlehofer at gmail.com>
@@ -67,12 +69,14 @@ kxrd <onyeabor at riseup.net>
 Lipin Dmitriy <blackwithwhite666 at gmail.com>
 Léo El Amri <leo at superlel.me>
 Marc Labranche <mlabranche at developertown.com>
+Marcel Brouwers <marcel at marcelbrouwers.nl>
 Marcelo Jorge Vieira <metal at alucinados.com>
 Marien Zwart <marienz at users.sourceforge.net>
 Mark Eichin
 Markus <nepenthesdev at gmail.com>
 Martin Muenstermann <mamuema at sourceforge.net>
 Matt King <matt at gnik.com>
+Michael C <michael at mchang.name>
 Michael Coughlin <michael.w.coughlin at gmail.com>
 Michael Treanor <26148512+skeptycal at users.noreply.github.com>
 Michał Górny <mgorny at gentoo.org>
@@ -103,6 +107,7 @@ Thomas Hunger <teh at camvine.org>
 Tino Lange <Tino.Lange at gmx.de>
 toddrme2178 <toddrme2178 at gmail.com>
 Tom Pierce <tom.pierce0 at gmail.com>
+Vesa Jääskeläinen <vesa.jaaskelainen at vaisala.com>
 Victor Lascurain <bittor at eleka.net>
 Vincent Philippon <Vincent.Philippon at ubisoft.com>
 Vitaly Murashev <vitaly.murashev at gmail.com>
index f8d9bbe14490b03375230a7a9c03ca87fd91eaf2..18aa760068fc316d2d24a1533c0958e68577e945 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+Version 7.45.3 [requires libcurl-7.19.0 or better] - 2024-02-17
+---------------------------------------------------------------
+
+        * Add CURLOPT_REQUEST_TARGET option (patch by Marcel Brouwers).
+        * Add missing 2nd parameters to METH_NOARGS functions
+          (patch by Scott Talbert).
+        * Add CURLOPT_AWS_SIGV4 option (patch by Scott Talbert).
+        * Add consistent names for newer Curl version constants
+          (patch by Scott Talbert).
+        * Only run HTTP version 3 option constant test if curl supported
+          (patch by Scott Talbert).
+        * Expose COMPILE_SSL_LIB in Python and use for test filtering
+          (patch by Scott Talbert).
+        * Filter tests based on *compile* libcurl version not runtime version
+          (patch by Scott Talbert).
+        * Use print function in callbacks documentation
+          (patch by Scott Talbert).
+        * Add missing shebang to tests/ext/test-suite.sh
+          (patch by Scott Talbert).
+        * Officially declare support for Python 3.12
+          (patch by Scott Talbert).
+        * Fix curl_multi_info_read flow that loses messages
+          (patch by Dom Sekotill).
+        * Support using environment variables for setup on Windows
+          (patch by Scott Talbert).
+        * Add support for Schannel SSL backend (patch by Scott Talbert)
+        * Skip HTTP2 tests based on a curl support check
+          (patch by Scott Talbert).
+        * Fix fake-curl tests so they work when run out of tree
+          (patch by Scott Talbert).
+        * xfail test_easy_pause_unpause unconditionally
+          (patch by Scott Talbert).
+        * Provide generic error strings in pycurl.error objects
+          (patch by Scott Talbert).
+        * Change URLs to new curl mailing list (patch by Michael C).
+        * Add missing HTTPS proxy options (patch by Jean Hominal).
+        * Add support for setting CURLOPT_SSLCERT_BLOB
+          (patch by Vesa Jääskeläinen).
+        * Add support for setting rest of CURLOPTTYPE_BLOB fields
+          (patch by Vesa Jääskeläinen).
+        * Build wheels on Linux/macOS/Windows (patch by Scott Talbert).
+
 Version 7.45.2 [requires libcurl-7.19.0 or better] - 2022-12-16
 ---------------------------------------------------------------
 
index 89b868ef2d2f9342bbf5b2750489525d8920448c..2a91aea7f58b794f5375973a042a6f02a09f52f2 100644 (file)
@@ -53,7 +53,7 @@ It will then fail at runtime as follows::
 
 To fix this, you need to tell ``setup.py`` what SSL backend is used::
 
-    python setup.py --with-[openssl|gnutls|nss|mbedtls|wolfssl|sectransp] install
+    python setup.py --with-[openssl|gnutls|nss|mbedtls|wolfssl|sectransp|schannel] install
 
 Note: as of PycURL 7.21.5, setup.py accepts ``--with-openssl`` option to
 indicate that libcurl is built against OpenSSL/LibreSSL/BoringSSL.
@@ -86,7 +86,7 @@ environment variable::
 The same applies to the SSL backend, if you need to specify it (see the SSL
 note above)::
 
-    export PYCURL_SSL_LIBRARY=[openssl|gnutls|nss|mbedtls|sectransp]
+    export PYCURL_SSL_LIBRARY=[openssl|gnutls|nss|mbedtls|sectransp|schannel]
     easy_install pycurl
 
 
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644 (file)
index ef2a1a0..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,112 +0,0 @@
-Metadata-Version: 2.1
-Name: pycurl
-Version: 7.45.2
-Summary: PycURL -- A Python Interface To The cURL library
-Home-page: http://pycurl.io/
-Author: Kjetil Jacobsen, Markus F.X.J. Oberhumer, Oleg Pudeyev
-Author-email: kjetilja@gmail.com, markus@oberhumer.com, oleg@bsdpower.com
-Maintainer: Oleg Pudeyev
-Maintainer-email: oleg@bsdpower.com
-License: LGPL/MIT
-Keywords: curl,libcurl,urllib,wget,download,file transfer,http,www
-Platform: All
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Web Environment
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: Microsoft :: Windows
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Topic :: Internet :: File Transfer Protocol (FTP)
-Classifier: Topic :: Internet :: WWW/HTTP
-Requires-Python: >=3.5
-License-File: COPYING-LGPL
-License-File: COPYING-MIT
-License-File: AUTHORS
-
-PycURL -- A Python Interface To The cURL library
-================================================
-
-PycURL is a Python interface to `libcurl`_, the multiprotocol file
-transfer library. Similarly to the urllib_ Python module,
-PycURL can be used to fetch objects identified by a URL from a Python program.
-Beyond simple fetches however PycURL exposes most of the functionality of
-libcurl, including:
-
-- Speed - libcurl is very fast and PycURL, being a thin wrapper above
-  libcurl, is very fast as well. PycURL `was benchmarked`_ to be several
-  times faster than requests_.
-- Features including multiple protocol support, SSL, authentication and
-  proxy options. PycURL supports most of libcurl's callbacks.
-- Multi_ and share_ interfaces.
-- Sockets used for network operations, permitting integration of PycURL
-  into the application's I/O loop (e.g., using Tornado_).
-
-.. _was benchmarked: http://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance
-.. _requests: http://python-requests.org/
-.. _Multi: https://curl.haxx.se/libcurl/c/libcurl-multi.html
-.. _share: https://curl.haxx.se/libcurl/c/libcurl-share.html
-.. _Tornado: http://www.tornadoweb.org/
-
-
-Requirements
-------------
-
-- Python 3.5-3.10.
-- libcurl 7.19.0 or better.
-
-
-Installation
-------------
-
-Download the source distribution from `PyPI`_.
-
-Please see `the installation documentation`_ for installation instructions.
-
-.. _PyPI: https://pypi.python.org/pypi/pycurl
-.. _the installation documentation: http://pycurl.io/docs/latest/install.html
-
-
-Documentation
--------------
-
-Documentation for the most recent PycURL release is available on
-`PycURL website <http://pycurl.io/docs/latest/>`_.
-
-
-Support
--------
-
-For support questions please use `curl-and-python mailing list`_.
-`Mailing list archives`_ are available for your perusal as well.
-
-Although not an official support venue, `Stack Overflow`_ has been
-popular with some PycURL users.
-
-Bugs can be reported `via GitHub`_. Please use GitHub only for bug
-reports and direct questions to our mailing list instead.
-
-.. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python
-.. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl
-.. _Mailing list archives: https://curl.haxx.se/mail/list.cgi?list=curl-and-python
-.. _via GitHub: https://github.com/pycurl/pycurl/issues
-
-
-License
--------
-
-PycURL is dual licensed under the LGPL and an MIT/X derivative license
-based on the libcurl license. The complete text of the licenses is available
-in COPYING-LGPL_ and COPYING-MIT_ files in the source distribution.
-
-.. _libcurl: https://curl.haxx.se/libcurl/
-.. _urllib: http://docs.python.org/library/urllib.html
-.. _COPYING-LGPL: https://raw.githubusercontent.com/pycurl/pycurl/master/COPYING-LGPL
-.. _COPYING-MIT: https://raw.githubusercontent.com/pycurl/pycurl/master/COPYING-MIT
diff --git a/README.kr.rst b/README.kr.rst
new file mode 100644 (file)
index 0000000..6428959
--- /dev/null
@@ -0,0 +1,180 @@
+PycURL -- cURL 라이브러리에 대한 파이썬 인터페이스
+================================================
+
+.. image:: https://api.travis-ci.org/pycurl/pycurl.png
+          :target: https://travis-ci.org/pycurl/pycurl
+
+.. image:: https://ci.appveyor.com/api/projects/status/q40v2q8r5d06bu92/branch/master?svg=true
+          :target: https://ci.appveyor.com/project/p/pycurl/branch/master
+
+PycURL은 멀티 프로토콜 파일 전송 라이브러리인 `libcurl`_, 에 대한 Python 인터페이스 입니다.
+urllib_ Python 모듈과 마찬가지로, PycURL 을 사용하여 Python프로그램에서 URL로 식별되는 객체를 가져올 수 있습니다.
+그러나 단순한 페치외에도 PycURL은 다음을 포함하여 libural의 기능을 대부분 보여줍니다.:
+
+- 속도 - libcurl은 매우 빠르며 libcurl보다 얇은 래퍼인 PycURL도 매우 빠릅니다.
+  PycURL은 요청_ 보다 몇 배 빠르다는`벤치마킹`_ 을 했습니다.
+-      여러 프로토콜 지원, SSL, 인증 및 프록시 옵션을 포함한 기능. PycURL은 대부분의 libcurl 콜백을 지원합니다.
+- 멀티_ 및 공유_ 인터페이스.
+- 네트워크 작업에 사용되는 소켓으로 PycURL을 응용 프로그램의 I / O 루프에 통합 할 수 있습니다 (e.g., Tornado_ 사용).
+
+.. _벤치마킹: http://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance
+.. _요청: http://python-requests.org/
+.. _멀티: https://curl.haxx.se/libcurl/c/libcurl-multi.html
+.. _공유: https://curl.haxx.se/libcurl/c/libcurl-share.html
+.. _Tornado: http://www.tornadoweb.org/
+
+
+요구 사항
+---------
+
+- Python 3.5-3.10.
+- libcurl 7.19.0 이상.
+
+
+설치
+----
+
+`PyPI`_ 에서 소스 및 바이너리 배포판을 다운로드 하십시오.
+이제 바이너리 휘을 32 비트 및 64 비트 Windows 버전에서 사용할 수 있습니다.
+
+설치 지침은 `INSTALL.rst`_ 를 참조하십시오. Git checkout에서 설치하는 경우, INSTALL.rst 의 `Git Checkout`_ 섹션의 지침을 따르십시오.
+
+.. _PyPI: https://pypi.python.org/pypi/pycurl
+.. _INSTALL.rst: http://pycurl.io/docs/latest/install.html
+.. _Git Checkout: http://pycurl.io/docs/latest/install.html#git-checkout
+
+
+문서
+----
+
+최신 PycURL 릴리즈에 대한 설명서는 `PycURL 웹사이트 <http://pycurl.io/docs/latest/>`_ 에서 구할 수 있습니다.
+
+PycURL 개발 버전에 대한 설명서는 `여기 <http://pycurl.io/docs/dev/>`_ 에서 볼 수 있습니다.
+
+소스에서 문서를 작성하려면 ``make docs``문서를 실행하십시오.
+작성하려면 `Sphinx <http://sphinx-doc.org/>`_ 를 설치해야하며 문서 문자열로 작성된 pycurl 확장 모듈도 설치해야 합니다.
+빌드된 문서는 ``build/doc`` 서브 디렉터리에 저장됩니다.
+
+
+지원
+----
+
+지원 질문은 `curl-and-python 메일링 목록`_을 사용 하십시오.
+`메일링 목록 보관소`_ 도 사용자의 사용을 위해 제공됩니다.
+
+공식 지원 장소는 아니지만, `Stack Overflow`_ 는 일부 PycURL 사용자에게 인기가 있습니다.
+
+버그는 `GitHub`_를 통해 보고될 수 있습니다. 버그 보고서와 GitHub는 메일링 목록에 직접 문의하십시오.
+
+.. _curl-and-python 메일링 목록: https://lists.haxx.se/listinfo/curl-and-python
+.. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl
+.. _메일링 목록 보관소: https://curl.haxx.se/mail/list.cgi?list=curl-and-python
+.. _GitHub: https://github.com/pycurl/pycurl/issues
+
+
+Automated Tests
+---------------
+
+PycURL comes with an automated test suite. To run the tests, execute::
+
+    make test
+
+The suite depends on packages `nose`_ and `bottle`_, as well as `vsftpd`_.
+
+Some tests use vsftpd configured to accept anonymous uploads. These tests
+are not run by default. As configured, vsftpd will allow reads and writes to
+anything the user running the tests has read and write access. To run
+vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so::
+
+    # use vsftpd in PATH
+    export PYCURL_VSFTPD_PATH=vsftpd
+
+    # specify full path to vsftpd
+    export PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd
+
+.. _nose: https://nose.readthedocs.org/
+.. _bottle: http://bottlepy.org/
+.. _vsftpd: http://vsftpd.beasts.org/
+
+
+Test Matrix
+-----------
+
+The test matrix is a separate framework that runs tests on more esoteric
+configurations. It supports:
+
+- Testing against Python 2.4, which bottle does not support.
+- Testing against Python compiled without threads, which requires an out of
+  process test server.
+- Testing against locally compiled libcurl with arbitrary options.
+
+To use the test matrix, first start the test server from Python 2.5+ by
+running::
+
+    python -m tests.appmanager
+
+Then in a different shell, and preferably in a separate user account,
+run the test matrix::
+
+    # run ftp tests, etc.
+    export PYCURL_VSFTPD_PATH=vsftpd
+    # create a new work directory, preferably not under pycurl tree
+    mkdir testmatrix
+    cd testmatrix
+    # run the matrix specifying absolute path
+    python /path/to/pycurl/tests/matrix.py
+
+The test matrix will download, build and install supported Python versions
+and supported libcurl versions, then run pycurl tests against each combination.
+To see what the combinations are, look in
+`tests/matrix.py <tests/matrix.py>`_.
+
+
+Contribute
+----------
+
+For smaller changes:
+
+#. Fork `the repository`_ on Github.
+#. Create a branch off **master**.
+#. Make your changes.
+#. Write a test which shows that the bug was fixed or that the feature
+   works as expected.
+#. Send a pull request.
+#. Check back after 10-15 minutes to see if tests passed on Travis CI.
+   PycURL supports old Python and libcurl releases and their support is tested
+   on Travis.
+
+For larger changes:
+
+#. Join the `mailing list`_.
+#. Discuss your proposal on the mailing list.
+#. When consensus is reached, implement it as described above.
+
+Please contribute binary distributions for your system to the
+`downloads repository`_.
+
+
+License
+-------
+
+::
+
+    Copyright (C) 2001-2008 by Kjetil Jacobsen <kjetilja at gmail.com>
+    Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer <markus at oberhumer.com>
+    Copyright (C) 2013-2022 by Oleg Pudeyev <code at olegp.name>
+
+    All rights reserved.
+
+    PycURL is dual licensed under the LGPL and an MIT/X derivative license
+    based on the cURL license.  A full copy of the LGPL license is included
+    in the file COPYING-LGPL.  A full copy of the MIT/X derivative license is
+    included in the file COPYING-MIT.  You can redistribute and/or modify PycURL
+    according to the terms of either license.
+
+.. _PycURL: http://pycurl.io/
+.. _libcurl: https://curl.haxx.se/libcurl/
+.. _urllib: http://docs.python.org/library/urllib.html
+.. _`the repository`: https://github.com/pycurl/pycurl
+.. _`mailing list`: https://lists.haxx.se/listinfo/curl-and-python
+.. _`downloads repository`: https://github.com/pycurl/downloads
index f4741570de7db0ee13b17a6fc7b7fe3d67fa957e..33df97cb7e2bf3e303ecd009e1052bff67868ec4 100644 (file)
@@ -76,7 +76,7 @@ popular with some PycURL users.
 Bugs can be reported `via GitHub`_. Please use GitHub only for bug
 reports and direct questions to our mailing list instead.
 
-.. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python
+.. _curl-and-python mailing list: https://lists.haxx.se/listinfo/curl-and-python
 .. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl
 .. _Mailing list archives: https://curl.haxx.se/mail/list.cgi?list=curl-and-python
 .. _via GitHub: https://github.com/pycurl/pycurl/issues
@@ -186,5 +186,5 @@ License
 .. _libcurl: https://curl.haxx.se/libcurl/
 .. _urllib: http://docs.python.org/library/urllib.html
 .. _`the repository`: https://github.com/pycurl/pycurl
-.. _`mailing list`: http://cool.haxx.se/mailman/listinfo/curl-and-python
+.. _`mailing list`: https://lists.haxx.se/listinfo/curl-and-python
 .. _`downloads repository`: https://github.com/pycurl/downloads
index e364d63c32ca8f155c984ffea945b03febaf0323..fe35a4cf0067f2cffd3965de9d522b5438bca395 100644 (file)
@@ -1,6 +1,12 @@
 Release Notes
 =============
 
+PycURL 7.45.3 - 2024-02-17
+--------------------------
+
+This release fixes several minor issues and adds support for several libcurl
+options.  Additionally, we are now building wheels for Linux/macOS/Windows.
+
 PycURL 7.45.2 - 2022-12-16
 --------------------------
 
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644 (file)
index 0000000..13f7716
--- /dev/null
@@ -0,0 +1,132 @@
+environment:
+  global:
+    # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
+    # /E:ON and /V:ON options are not enabled in the batch script interpreter
+    # See: http://stackoverflow.com/a/13751649/163740
+    CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\.appveyor\\run_with_env.cmd"
+    # The pip version upgrade message on stderr causes appveyor to fail steps.
+    PIP_DISABLE_PIP_VERSION_CHECK: "1"
+
+  matrix:
+
+    - PYTHON: "C:\\Python35"
+      PYTHON_VERSION: "3.5.x" # currently 3.5.3
+      PYTHON_ARCH: "32"
+
+    - PYTHON: "C:\\Python35-x64"
+      PYTHON_VERSION: "3.5.x" # currently 3.5.3
+      PYTHON_ARCH: "64"
+
+    - PYTHON: "C:\\Python36"
+      PYTHON_VERSION: "3.6.x" # currently 3.6.5
+      PYTHON_ARCH: "32"
+
+    - PYTHON: "C:\\Python36-x64"
+      PYTHON_VERSION: "3.6.x" # currently 3.6.5
+      PYTHON_ARCH: "64"
+
+    - PYTHON: "C:\\Python37"
+      PYTHON_VERSION: "3.7.x"
+      PYTHON_ARCH: "32"
+
+    - PYTHON: "C:\\Python37-x64"
+      PYTHON_VERSION: "3.7.x"
+      PYTHON_ARCH: "64"
+
+    - PYTHON: "C:\\Python38"
+      PYTHON_VERSION: "3.8.x"
+      PYTHON_ARCH: "32"
+
+    - PYTHON: "C:\\Python38-x64"
+      PYTHON_VERSION: "3.8.x"
+      PYTHON_ARCH: "64"
+
+    - PYTHON: "C:\\Python39"
+      PYTHON_VERSION: "3.9.1"
+      PYTHON_ARCH: "32"
+
+    - PYTHON: "C:\\Python39-x64"
+      PYTHON_VERSION: "3.9.1"
+      PYTHON_ARCH: "64"
+
+install:
+
+  #- "%CMD_IN_ENV% set"
+
+  # If there is a newer build queued for the same PR, cancel this one.
+  # The AppVeyor 'rollout builds' option is supposed to serve the same
+  # purpose but it is problematic because it tends to cancel builds pushed
+  # directly to master instead of just PR builds (or the converse).
+  # credits: JuliaLang developers.
+  - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
+        https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
+        Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
+          throw "There are newer queued builds for this pull request, failing early." }
+  #- ECHO "Filesystem root:"
+  #- ps: "ls \"C:/\""
+
+  #- ECHO "Installed SDKs:"
+  #- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
+
+  # Install Python (from the official .msi of http://python.org) and pip when
+  # not already installed.
+  - ps: if (-not(Test-Path($env:PYTHON))) { & .appveyor\install.ps1 }
+
+  # Prepend newly installed Python to the PATH of this build (this cannot be
+  # done from inside the powershell script as it would require to restart
+  # the parent CMD process).
+  - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
+
+  # Check that we have the expected version and architecture for Python
+  - "python --version"
+  - "python -c \"import struct; print(struct.calcsize('P') * 8)\""
+
+  # Install the build dependencies of the project. If some dependencies contain
+  # compiled extensions and are not provided as pre-built wheel packages,
+  # pip will build them from source using the MSVC compiler matching the
+  # target Python version and architecture
+  - "%CMD_IN_ENV% python -m pip install --upgrade setuptools"
+  - "%CMD_IN_ENV% pip install -r requirements-dev.txt"
+  - "%CMD_IN_ENV% pip install wheel"
+
+  # Build the compiled extension
+  - "%CMD_IN_ENV% python winbuild.py getdeps"
+
+build_script:
+  #-  call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
+  # Build the compiled extension
+  - "%CMD_IN_ENV% python setup.py docstrings"
+  - "%CMD_IN_ENV% python setup.py --with-openssl --libcurl-lib-name=libcurl_a.lib --openssl-lib-name=\"\" --curl-dir=deps bdist_wheel bdist_wininst bdist_msi bdist_egg --link-arg=/LIBPATH:deps/lib --link-arg=zlib.lib --link-arg=libcrypto.lib --link-arg=libssl.lib --link-arg=crypt32.lib --link-arg=advapi32.lib --link-arg=libcares.lib --link-arg=libssh2.lib --link-arg=nghttp2_static.lib --link-arg=normaliz.lib --link-arg=user32.lib"
+
+test_script:
+  # Run the project tests
+  - ps: $whl = get-childitem dist\*.whl;
+        pip install $whl
+  - pytest
+
+after_test:
+  # If tests are successful, create binary packages for the project.
+  #- "%CMD_IN_ENV% python setup.py bdist_wheel"
+  #- "%CMD_IN_ENV% python setup.py bdist_wininst"
+  #- "%CMD_IN_ENV% python setup.py bdist_msi"
+  - ps: "ls dist"
+
+artifacts:
+  # Archive the generated packages in the ci.appveyor.com build report.
+  - path: dist\*
+
+on_success:
+  # below are 4 environment variables that you can set to allow twine to upload
+  # these should be set from the appveyor web gui and not in the script. and
+  # only set them when you are doing a release
+  # TWINE_USERNAME
+  # TWINE_PASSWORD
+  # TWINE_REPOSITORY_URL
+  # TWINE_CERT
+  - ps: >-
+      if (Test-Path 'ENV:TWINE_PASSWORD') {
+          pip install twine
+          python -m twine upload dist/*
+      } else {
+          "Skipping twine upload."
+      }
index afe25946299a79f1bfcebbd58cc4af64df9e13fb..c19558c9419d33397a0c1eab3ba4695b076741c2 100644 (file)
@@ -211,7 +211,7 @@ enabled for this callback to be invoked.
 ::
 
     def test(debug_type, debug_msg):
-        print "debug(%d): %s" % (debug_type, debug_msg)
+        print("debug(%d): %s" % (debug_type, debug_msg))
 
     c = pycurl.Curl()
     c.setopt(pycurl.URL, "https://curl.haxx.se/")
@@ -261,10 +261,10 @@ document, the arguments related to uploads are zero, and vice versa.
 
     ## Callback function invoked when download/upload has progress
     def progress(download_t, download_d, upload_t, upload_d):
-        print "Total to download", download_t
-        print "Total downloaded", download_d
-        print "Total to upload", upload_t
-        print "Total uploaded", upload_d
+        print("Total to download", download_t)
+        print("Total downloaded", download_d)
+        print("Total to upload", upload_t)
+        print("Total uploaded", upload_d)
 
     c = pycurl.Curl()
     c.setopt(c.URL, "http://slashdot.org/")
index 9f98d713f1cf1e944f3747a1ed36aa9cae87f1c5..bf40fae4a98f8d7dd7495e58e4df7c83b4c5c1f1 100644 (file)
@@ -54,9 +54,9 @@ copyright = u'2001-2022 Kjetil Jacobsen, Markus F.X.J. Oberhumer, Oleg Pudeyev'
 # built documents.
 #
 # The short X.Y version.
-version = '7.45.2'
+version = '7.45.3'
 # The full version, including alpha/beta/rc tags.
-release = '7.45.2'
+release = '7.45.3'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
index 874958f6f2c6fb368361c6d078be611b791f9bd9..85a6b22e1057099ca3849d69f33d5dc9b497202a 100644 (file)
@@ -118,7 +118,7 @@ certain you have found a bug in PycURL. If you do not have a patch to fix
 the bug, or at least a specific code fragment in PycURL that you believe is
 the cause, you should instead post your inquiry to the mailing list.
 
-.. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python
+.. _curl-and-python mailing list: https://lists.haxx.se/listinfo/curl-and-python
 .. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl
 .. _Mailing list archives: https://curl.haxx.se/mail/list.cgi?list=curl-and-python
 .. _via GitHub: https://github.com/pycurl/pycurl/issues
diff --git a/pycurl.egg-info/PKG-INFO b/pycurl.egg-info/PKG-INFO
deleted file mode 100644 (file)
index ef2a1a0..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-Metadata-Version: 2.1
-Name: pycurl
-Version: 7.45.2
-Summary: PycURL -- A Python Interface To The cURL library
-Home-page: http://pycurl.io/
-Author: Kjetil Jacobsen, Markus F.X.J. Oberhumer, Oleg Pudeyev
-Author-email: kjetilja@gmail.com, markus@oberhumer.com, oleg@bsdpower.com
-Maintainer: Oleg Pudeyev
-Maintainer-email: oleg@bsdpower.com
-License: LGPL/MIT
-Keywords: curl,libcurl,urllib,wget,download,file transfer,http,www
-Platform: All
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Web Environment
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: Microsoft :: Windows
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Topic :: Internet :: File Transfer Protocol (FTP)
-Classifier: Topic :: Internet :: WWW/HTTP
-Requires-Python: >=3.5
-License-File: COPYING-LGPL
-License-File: COPYING-MIT
-License-File: AUTHORS
-
-PycURL -- A Python Interface To The cURL library
-================================================
-
-PycURL is a Python interface to `libcurl`_, the multiprotocol file
-transfer library. Similarly to the urllib_ Python module,
-PycURL can be used to fetch objects identified by a URL from a Python program.
-Beyond simple fetches however PycURL exposes most of the functionality of
-libcurl, including:
-
-- Speed - libcurl is very fast and PycURL, being a thin wrapper above
-  libcurl, is very fast as well. PycURL `was benchmarked`_ to be several
-  times faster than requests_.
-- Features including multiple protocol support, SSL, authentication and
-  proxy options. PycURL supports most of libcurl's callbacks.
-- Multi_ and share_ interfaces.
-- Sockets used for network operations, permitting integration of PycURL
-  into the application's I/O loop (e.g., using Tornado_).
-
-.. _was benchmarked: http://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance
-.. _requests: http://python-requests.org/
-.. _Multi: https://curl.haxx.se/libcurl/c/libcurl-multi.html
-.. _share: https://curl.haxx.se/libcurl/c/libcurl-share.html
-.. _Tornado: http://www.tornadoweb.org/
-
-
-Requirements
-------------
-
-- Python 3.5-3.10.
-- libcurl 7.19.0 or better.
-
-
-Installation
-------------
-
-Download the source distribution from `PyPI`_.
-
-Please see `the installation documentation`_ for installation instructions.
-
-.. _PyPI: https://pypi.python.org/pypi/pycurl
-.. _the installation documentation: http://pycurl.io/docs/latest/install.html
-
-
-Documentation
--------------
-
-Documentation for the most recent PycURL release is available on
-`PycURL website <http://pycurl.io/docs/latest/>`_.
-
-
-Support
--------
-
-For support questions please use `curl-and-python mailing list`_.
-`Mailing list archives`_ are available for your perusal as well.
-
-Although not an official support venue, `Stack Overflow`_ has been
-popular with some PycURL users.
-
-Bugs can be reported `via GitHub`_. Please use GitHub only for bug
-reports and direct questions to our mailing list instead.
-
-.. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python
-.. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl
-.. _Mailing list archives: https://curl.haxx.se/mail/list.cgi?list=curl-and-python
-.. _via GitHub: https://github.com/pycurl/pycurl/issues
-
-
-License
--------
-
-PycURL is dual licensed under the LGPL and an MIT/X derivative license
-based on the libcurl license. The complete text of the licenses is available
-in COPYING-LGPL_ and COPYING-MIT_ files in the source distribution.
-
-.. _libcurl: https://curl.haxx.se/libcurl/
-.. _urllib: http://docs.python.org/library/urllib.html
-.. _COPYING-LGPL: https://raw.githubusercontent.com/pycurl/pycurl/master/COPYING-LGPL
-.. _COPYING-MIT: https://raw.githubusercontent.com/pycurl/pycurl/master/COPYING-MIT
diff --git a/pycurl.egg-info/SOURCES.txt b/pycurl.egg-info/SOURCES.txt
deleted file mode 100644 (file)
index 1c92627..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-AUTHORS
-COPYING-LGPL
-COPYING-MIT
-ChangeLog
-INSTALL.rst
-MANIFEST.in
-Makefile
-README.rst
-RELEASE-NOTES.rst
-pytest.ini
-requirements-dev.txt
-setup.py
-winbuild.py
-doc/callbacks.rst
-doc/conf.py
-doc/curl.rst
-doc/curlmultiobject.rst
-doc/curlobject.rst
-doc/curlshareobject.rst
-doc/files.rst
-doc/index.rst
-doc/install.rst
-doc/internals.rst
-doc/pycurl.rst
-doc/quickstart.rst
-doc/release-notes.rst
-doc/release-process.rst
-doc/thread-safety.rst
-doc/troubleshooting.rst
-doc/unicode.rst
-doc/unimplemented.rst
-doc/docstrings/curl.rst
-doc/docstrings/curl_close.rst
-doc/docstrings/curl_duphandle.rst
-doc/docstrings/curl_errstr.rst
-doc/docstrings/curl_errstr_raw.rst
-doc/docstrings/curl_getinfo.rst
-doc/docstrings/curl_getinfo_raw.rst
-doc/docstrings/curl_pause.rst
-doc/docstrings/curl_perform.rst
-doc/docstrings/curl_perform_rb.rst
-doc/docstrings/curl_perform_rs.rst
-doc/docstrings/curl_reset.rst
-doc/docstrings/curl_set_ca_certs.rst
-doc/docstrings/curl_setopt.rst
-doc/docstrings/curl_setopt_string.rst
-doc/docstrings/curl_unsetopt.rst
-doc/docstrings/multi.rst
-doc/docstrings/multi_add_handle.rst
-doc/docstrings/multi_assign.rst
-doc/docstrings/multi_close.rst
-doc/docstrings/multi_fdset.rst
-doc/docstrings/multi_info_read.rst
-doc/docstrings/multi_perform.rst
-doc/docstrings/multi_remove_handle.rst
-doc/docstrings/multi_select.rst
-doc/docstrings/multi_setopt.rst
-doc/docstrings/multi_socket_action.rst
-doc/docstrings/multi_socket_all.rst
-doc/docstrings/multi_timeout.rst
-doc/docstrings/pycurl_global_cleanup.rst
-doc/docstrings/pycurl_global_init.rst
-doc/docstrings/pycurl_module.rst
-doc/docstrings/pycurl_version_info.rst
-doc/docstrings/share.rst
-doc/docstrings/share_close.rst
-doc/docstrings/share_setopt.rst
-doc/static/favicon.ico
-examples/basicfirst.py
-examples/file_upload.py
-examples/linksys.py
-examples/multi-socket_action-select.py
-examples/opensocketexception.py
-examples/retriever-multi.py
-examples/retriever.py
-examples/sfquery.py
-examples/smtp.py
-examples/ssh_keyfunction.py
-examples/xmlrpc_curl.py
-examples/quickstart/file_upload_buffer.py
-examples/quickstart/file_upload_real.py
-examples/quickstart/file_upload_real_fancy.py
-examples/quickstart/follow_redirect.py
-examples/quickstart/form_post.py
-examples/quickstart/get.py
-examples/quickstart/get_python2.py
-examples/quickstart/get_python2_https.py
-examples/quickstart/get_python3.py
-examples/quickstart/get_python3_https.py
-examples/quickstart/put_buffer.py
-examples/quickstart/put_file.py
-examples/quickstart/response_headers.py
-examples/quickstart/response_info.py
-examples/quickstart/write_file.py
-examples/tests/test_build_config.py
-examples/tests/test_gtk.py
-examples/tests/test_xmlrpc.py
-pycurl.egg-info/PKG-INFO
-pycurl.egg-info/SOURCES.txt
-pycurl.egg-info/dependency_links.txt
-pycurl.egg-info/top_level.txt
-python/curl/__init__.py
-src/docstrings.c
-src/docstrings.h
-src/easy.c
-src/easycb.c
-src/easyinfo.c
-src/easyopt.c
-src/easyperform.c
-src/module.c
-src/multi.c
-src/oscompat.c
-src/pycurl.h
-src/pythoncompat.c
-src/share.c
-src/stringcompat.c
-src/threadsupport.c
-src/util.c
-tests/__init__.py
-tests/app.py
-tests/appmanager.py
-tests/cadata_test.py
-tests/certinfo_test.py
-tests/close_socket_cb_test.py
-tests/curl_object_test.py
-tests/debug_test.py
-tests/default_write_cb_test.py
-tests/duphandle_test.py
-tests/error_constants_test.py
-tests/error_test.py
-tests/failonerror_test.py
-tests/ftp_test.py
-tests/getinfo_test.py
-tests/global_init_test.py
-tests/header_cb_test.py
-tests/header_test.py
-tests/high_level_curl_test.py
-tests/info_constants_test.py
-tests/info_test.py
-tests/internals_test.py
-tests/matrix.py
-tests/memory_mgmt_test.py
-tests/multi_callback_test.py
-tests/multi_memory_mgmt_test.py
-tests/multi_option_constants_test.py
-tests/multi_socket_select_test.py
-tests/multi_socket_test.py
-tests/multi_test.py
-tests/multi_timer_test.py
-tests/open_socket_cb_test.py
-tests/option_constants_test.py
-tests/pause_test.py
-tests/perform_test.py
-tests/post_test.py
-tests/procmgr.py
-tests/protocol_constants_test.py
-tests/read_cb_test.py
-tests/readdata_test.py
-tests/relative_url_test.py
-tests/reload_test.py
-tests/reset_test.py
-tests/resolve_test.py
-tests/run-quickstart.sh
-tests/run.sh
-tests/runwsgi.py
-tests/seek_cb_constants_test.py
-tests/seek_cb_test.py
-tests/setopt_lifecycle_test.py
-tests/setopt_string_test.py
-tests/setopt_test.py
-tests/setopt_unicode_test.py
-tests/setup_test.py
-tests/share_test.py
-tests/sockopt_cb_test.py
-tests/ssh_key_cb_test.py
-tests/subclass_test.py
-tests/unset_range_test.py
-tests/user_agent_string_test.py
-tests/util.py
-tests/version_comparison_test.py
-tests/version_constants_test.py
-tests/version_test.py
-tests/vsftpd.conf
-tests/weakref_test.py
-tests/write_abort_test.py
-tests/write_cb_bogus_test.py
-tests/write_test.py
-tests/write_to_stringio_test.py
-tests/xferinfo_cb_test.py
-tests/certs/ca.crt
-tests/certs/ca.key
-tests/certs/server.crt
-tests/certs/server.key
-tests/ext/test-lib.sh
-tests/ext/test-suite.sh
-tests/fake-curl/curl-config-empty
-tests/fake-curl/curl-config-libs-and-static-libs
-tests/fake-curl/curl-config-ssl-feature-only
-tests/fake-curl/curl-config-ssl-in-libs
-tests/fake-curl/curl-config-ssl-in-static-libs
-tests/fake-curl/libcurl/Makefile
-tests/fake-curl/libcurl/with_gnutls.c
-tests/fake-curl/libcurl/with_nss.c
-tests/fake-curl/libcurl/with_openssl.c
-tests/fake-curl/libcurl/with_unknown_ssl.c
-tests/fake-curl/libcurl/without_ssl.c
-tests/fixtures/form_submission.txt
-tests/matrix/curl-7.19.0-sslv2-2b0e09b0f98.patch
-tests/matrix/curl-7.19.0-sslv2-c66b0b32fba-modified.patch
-tests/matrix/openssl-1.0.1e-fix_pod_syntax-1.patch
-winbuild/__init__.py
-winbuild/builder.py
-winbuild/c-ares-vs2015.patch
-winbuild/cares.py
-winbuild/config.py
-winbuild/curl.py
-winbuild/iconv.py
-winbuild/idn.py
-winbuild/libcurl-fix-zlib-references.patch
-winbuild/libssh2-vs2015.patch
-winbuild/nghttp_cmake.py
-winbuild/nghttp_gmake.py
-winbuild/openssl-fix-crt-1.0.2.patch
-winbuild/openssl-fix-crt-1.1.0.patch
-winbuild/openssl-fix-crt-1.1.1.patch
-winbuild/openssl.py
-winbuild/pycurl.py
-winbuild/pythons.py
-winbuild/ssh.py
-winbuild/tools.py
-winbuild/utils.py
-winbuild/vcvars-vc14-32.sh
-winbuild/vcvars-vc14-64.sh
-winbuild/zlib.py
\ No newline at end of file
diff --git a/pycurl.egg-info/dependency_links.txt b/pycurl.egg-info/dependency_links.txt
deleted file mode 100644 (file)
index 8b13789..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/pycurl.egg-info/top_level.txt b/pycurl.egg-info/top_level.txt
deleted file mode 100644 (file)
index 62b667f..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-curl
-pycurl
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644 (file)
index 0000000..1fd2c85
--- /dev/null
@@ -0,0 +1,29 @@
+[tool.cibuildwheel]
+build = "cp3*"
+skip = ["cp36-*", "cp37-*", "*-musllinux*"]
+manylinux-aarch64-image = "manylinux_2_28"
+manylinux-x86_64-image = "manylinux_2_28"
+build-frontend = "build"
+build-verbosity = 1
+test-command = "pytest -v -rs {project}/tests"
+
+[tool.cibuildwheel.linux]
+archs = ["x86_64", "aarch64"]
+before-all = "yum install -y libcurl-devel openssl-devel"
+before-test = [
+    "pip install flake8 -r requirements-dev.txt",
+    "make -C {package}/tests/fake-curl/libcurl",
+]
+test-command = "pytest -v -rs {project}/tests -k \"not test_keyfunction\""
+
+[tool.cibuildwheel.macos]
+archs = ["all"]
+environment = "PYCURL_CURL_CONFIG=/usr/bin/curl-config PYCURL_SSL_LIBRARY=sectransp"
+before-test = [
+    "pip install flake8 -r requirements-dev.txt",
+    "CFLAGS=\"-arch x86_64 -arch arm64\" make -C {package}/tests/fake-curl/libcurl",
+]
+
+[tool.cibuildwheel.windows]
+before-build = "pip install delvewheel"
+before-test = "pip install flake8 -r requirements-dev.txt"
index da562d54f6c235521145ae45763b647fa0368a8c..0c89c6d531d8e609998d52e798066b4843dd2b98 100644 (file)
@@ -5,3 +5,4 @@ flaky
 pyflakes
 pytest>=5
 sphinx
+setuptools
diff --git a/scripts/missing-symbols b/scripts/missing-symbols
new file mode 100755 (executable)
index 0000000..589091f
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+import sys, re, subprocess
+
+def process(siv_path):
+    with open(siv_path) as f:
+        for line in f:
+            if line[0] == ' ':
+                # comment
+                continue
+            line = line.strip()
+            if line == '':
+                continue
+            parts = re.split(r'\s+', line)
+            if len(parts) >= 4:
+                # removed symbol, all are very old
+                continue
+            if parts[0] == 'CURLOPT_CLOSEPOLICY' or \
+                parts[0].startswith('CURLCLOSEPOLICY_') or \
+                parts[0] == 'CURLOPT_WRITEINFO':
+                    # no docs for these options
+                    continue
+            try:
+                subprocess.check_call(['git', 'grep', '-q', parts[0], 'src'])
+            except subprocess.CalledProcessError:
+                print('Missing %s (since %s)' % (parts[0], parts[1]))
+
+process(sys.argv[1])
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644 (file)
index 8bfd5a1..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-[egg_info]
-tag_build = 
-tag_date = 0
-
index a5b115b0515cbda433ea20dfcec398d403d10737..f764f8fa782acfde41f79af39d9d01b53a548d6b 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@
 
 PACKAGE = "pycurl"
 PY_PACKAGE = "curl"
-VERSION = "7.45.2"
+VERSION = "7.45.3"
 
 import glob, os, re, shlex, sys, subprocess
 from setuptools import setup
@@ -410,13 +410,15 @@ ignore this message.''')
         return ssl_lib_detected
 
     def configure_windows(self):
-        OPENSSL_DIR = scan_argv(self.argv, "--openssl-dir=")
+        OPENSSL_DIR = os.environ.get('PYCURL_OPENSSL_DIR')
+        OPENSSL_DIR = scan_argv(self.argv, "--openssl-dir=", OPENSSL_DIR)
         if OPENSSL_DIR is not None:
             self.include_dirs.append(os.path.join(OPENSSL_DIR, "include"))
             self.library_dirs.append(os.path.join(OPENSSL_DIR, "lib"))
         # Windows users have to pass --curl-dir parameter to specify path
         # to libcurl, because there is no curl-config on windows at all.
-        curl_dir = scan_argv(self.argv, "--curl-dir=")
+        curl_dir = os.environ.get('PYCURL_CURL_DIR')
+        curl_dir = scan_argv(self.argv, "--curl-dir=", curl_dir)
         if curl_dir is None:
             fail("Please specify --curl-dir=/path/to/built/libcurl")
         if not os.path.exists(curl_dir):
@@ -431,18 +433,25 @@ ignore this message.''')
         # For libcurl 7.46.0, the library name is libcurl.lib.
         # And static library name is libcurl_a.lib by default as of libcurl 7.46.0.
         # override with: --libcurl-lib-name=libcurl_imp.lib
-        curl_lib_name = scan_argv(self.argv, '--libcurl-lib-name=', 'libcurl.lib')
+        curl_lib_name = os.environ.get('PYCURL_LIBCURL_LIB_NAME', 'libcurl.lib')
+        curl_lib_name = scan_argv(self.argv, '--libcurl-lib-name=', curl_lib_name)
 
         # openssl 1.1.0 changed its library names
         # from libeay32.lib/ssleay32.lib to libcrypto.lib/libssl.lib.
         # at the same time they dropped thread locking callback interface,
         # meaning the correct usage of this option is --openssl-lib-name=""
-        self.openssl_lib_name = scan_argv(self.argv, '--openssl-lib-name=', 'libeay32.lib')
+        self.openssl_lib_name = os.environ.get('PYCURL_OPENSSL_LIB_NAME', 'libeay32.lib')
+        self.openssl_lib_name = scan_argv(self.argv, '--openssl-lib-name=', self.openssl_lib_name)
 
+        try:
+            for lib in os.environ['PYCURL_LINK_ARG'].split(os.pathsep):
+                self.extra_link_args.append(lib)
+        except KeyError:
+            pass
         for lib in scan_argvs(self.argv, '--link-arg='):
             self.extra_link_args.append(lib)
 
-        if scan_argv(self.argv, "--use-libcurl-dll") is not None:
+        if scan_argv(self.argv, "--use-libcurl-dll") is not None or os.environ.get('PYCURL_USE_LIBCURL_DLL') is not None:
             libcurl_lib_path = os.path.join(curl_dir, "lib", curl_lib_name)
             self.extra_link_args.extend(["ws2_32.lib"])
             if str.find(sys.version, "MSC") >= 0:
@@ -459,6 +468,14 @@ ignore this message.''')
 
         if scan_argv(self.argv, '--with-openssl') is not None or scan_argv(self.argv, '--with-ssl') is not None:
             self.using_openssl()
+        elif scan_argv(self.argv, '--with-schannel') is not None:
+            self.using_schannel()
+        elif 'PYCURL_SSL_LIBRARY' in os.environ:
+            ssl_lib = os.environ['PYCURL_SSL_LIBRARY']
+            if ssl_lib in ['openssl', 'schannel']:
+                getattr(self, 'using_%s' % ssl_lib)()
+            else:
+                raise ConfigurationError('Invalid value "%s" for PYCURL_SSL_LIBRARY' % ssl_lib)
 
         self.check_avoid_stdio()
 
@@ -580,12 +597,18 @@ ignore this message.''')
         self.define_macros.append(('HAVE_CURL_SSL', 1))
         self.ssl_lib_detected = 'sectransp'
 
+    def using_schannel(self):
+        self.define_macros.append(('HAVE_CURL_SCHANNEL', 1))
+        self.define_macros.append(('HAVE_CURL_SSL', 1))
+        self.ssl_lib_detected = 'schannel'
+
 
 def strip_pycurl_options(argv):
     if sys.platform == 'win32':
         options = [
             '--curl-dir=', '--libcurl-lib-name=', '--use-libcurl-dll',
             '--avoid-stdio', '--with-openssl', '--openssl-dir=',
+            '--with-schannel',
         ]
     else:
         options = ['--openssl-dir=', '--curl-config=', '--avoid-stdio']
@@ -603,6 +626,7 @@ PRETTY_SSL_LIBS = {
     'nss': 'NSS',
     'mbedtls': 'mbedTLS',
     'sectransp': 'Secure Transport',
+    'schannel': 'Schannel',
 }
 
 def get_extension(argv, split_extension_source=False):
@@ -828,7 +852,7 @@ libcurl, including:
 Requirements
 ------------
 
-- Python 3.5-3.10.
+- Python 3.5-3.12.
 - libcurl 7.19.0 or better.
 
 
@@ -862,7 +886,7 @@ popular with some PycURL users.
 Bugs can be reported `via GitHub`_. Please use GitHub only for bug
 reports and direct questions to our mailing list instead.
 
-.. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python
+.. _curl-and-python mailing list: https://lists.haxx.se/listinfo/curl-and-python
 .. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl
 .. _Mailing list archives: https://curl.haxx.se/mail/list.cgi?list=curl-and-python
 .. _via GitHub: https://github.com/pycurl/pycurl/issues
@@ -903,6 +927,8 @@ in COPYING-LGPL_ and COPYING-MIT_ files in the source distribution.
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
+        'Programming Language :: Python :: 3.12',
         'Topic :: Internet :: File Transfer Protocol (FTP)',
         'Topic :: Internet :: WWW/HTTP',
     ],
@@ -936,6 +962,7 @@ PycURL Windows options:
  --with-openssl                        libcurl is linked against OpenSSL/LibreSSL/BoringSSL
  --with-ssl                            legacy alias for --with-openssl
  --link-arg=foo.lib                    also link against specified library
+ --with-schannel                       libcurl is linked against Schannel
 '''
 
 if __name__ == "__main__":
diff --git a/src/docstrings.c b/src/docstrings.c
deleted file mode 100644 (file)
index 3f56578..0000000
+++ /dev/null
@@ -1,748 +0,0 @@
-/* Generated file - do not edit. */
-/* See doc/docstrings/ *.rst. */
-
-#include "pycurl.h"
-
-PYCURL_INTERNAL const char curl_doc[] = "Curl() -> New Curl object\n\
-\n\
-Creates a new :ref:`curlobject` which corresponds to a\n\
-``CURL`` handle in libcurl. Curl objects automatically set\n\
-CURLOPT_VERBOSE to 0, CURLOPT_NOPROGRESS to 1, provide a default\n\
-CURLOPT_USERAGENT and setup CURLOPT_ERRORBUFFER to point to a\n\
-private error buffer.\n\
-\n\
-Implicitly calls :py:func:`pycurl.global_init` if the latter has not yet been called.";
-
-PYCURL_INTERNAL const char curl_close_doc[] = "close() -> None\n\
-\n\
-Close handle and end curl session.\n\
-\n\
-Corresponds to `curl_easy_cleanup`_ in libcurl. This method is\n\
-automatically called by pycurl when a Curl object no longer has any\n\
-references to it, but can also be called explicitly.\n\
-\n\
-.. _curl_easy_cleanup:\n\
-    https://curl.haxx.se/libcurl/c/curl_easy_cleanup.html";
-
-PYCURL_INTERNAL const char curl_duphandle_doc[] = "duphandle() -> Curl\n\
-\n\
-Clone a curl handle. This function will return a new curl handle,\n\
-a duplicate, using all the options previously set in the input curl handle.\n\
-Both handles can subsequently be used independently.\n\
-\n\
-The new handle will not inherit any state information, no connections,\n\
-no SSL sessions and no cookies. It also will not inherit any share object\n\
-states or options (it will be made as if SHARE was unset).\n\
-\n\
-Corresponds to `curl_easy_duphandle`_ in libcurl.\n\
-\n\
-Example usage::\n\
-\n\
-    import pycurl\n\
-    curl = pycurl.Curl()\n\
-    curl.setopt(pycurl.URL, \"https://python.org\")\n\
-    dup = curl.duphandle()\n\
-    curl.perform()\n\
-    dup.perform()\n\
-\n\
-.. _curl_easy_duphandle:\n\
-    https://curl.se/libcurl/c/curl_easy_duphandle.html";
-
-PYCURL_INTERNAL const char curl_errstr_doc[] = "errstr() -> string\n\
-\n\
-Return the internal libcurl error buffer of this handle as a string.\n\
-\n\
-Return value is a ``str`` instance on all Python versions.\n\
-On Python 3, error buffer data is decoded using Python's default encoding\n\
-at the time of the call. If this decoding fails, ``UnicodeDecodeError`` is\n\
-raised. Use :ref:`errstr_raw <errstr_raw>` to retrieve the error buffer\n\
-as a byte string in this case.\n\
-\n\
-On Python 2, ``errstr`` and ``errstr_raw`` behave identically.";
-
-PYCURL_INTERNAL const char curl_errstr_raw_doc[] = "errstr_raw() -> byte string\n\
-\n\
-Return the internal libcurl error buffer of this handle as a byte string.\n\
-\n\
-Return value is a ``str`` instance on Python 2 and ``bytes`` instance\n\
-on Python 3. Unlike :ref:`errstr_raw <errstr_raw>`, ``errstr_raw``\n\
-allows reading libcurl error buffer in Python 3 when its contents is not\n\
-valid in Python's default encoding.\n\
-\n\
-On Python 2, ``errstr`` and ``errstr_raw`` behave identically.\n\
-\n\
-*Added in version 7.43.0.2.*";
-
-PYCURL_INTERNAL const char curl_getinfo_doc[] = "getinfo(option) -> Result\n\
-\n\
-Extract and return information from a curl session,\n\
-decoding string data in Python's default encoding at the time of the call.\n\
-Corresponds to `curl_easy_getinfo`_ in libcurl.\n\
-The ``getinfo`` method should not be called unless\n\
-``perform`` has been called and finished.\n\
-\n\
-*option* is a constant corresponding to one of the\n\
-``CURLINFO_*`` constants in libcurl. Most option constant names match\n\
-the respective ``CURLINFO_*`` constant names with the ``CURLINFO_`` prefix\n\
-removed, for example ``CURLINFO_CONTENT_TYPE`` is accessible as\n\
-``pycurl.CONTENT_TYPE``. Exceptions to this rule are as follows:\n\
-\n\
-- ``CURLINFO_FILETIME`` is mapped as ``pycurl.INFO_FILETIME``\n\
-- ``CURLINFO_COOKIELIST`` is mapped as ``pycurl.INFO_COOKIELIST``\n\
-- ``CURLINFO_CERTINFO`` is mapped as ``pycurl.INFO_CERTINFO``\n\
-- ``CURLINFO_RTSP_CLIENT_CSEQ`` is mapped as ``pycurl.INFO_RTSP_CLIENT_CSEQ``\n\
-- ``CURLINFO_RTSP_CSEQ_RECV`` is mapped as ``pycurl.INFO_RTSP_CSEQ_RECV``\n\
-- ``CURLINFO_RTSP_SERVER_CSEQ`` is mapped as ``pycurl.INFO_RTSP_SERVER_CSEQ``\n\
-- ``CURLINFO_RTSP_SESSION_ID`` is mapped as ``pycurl.INFO_RTSP_SESSION_ID``\n\
-\n\
-The type of return value depends on the option, as follows:\n\
-\n\
-- Options documented by libcurl to return an integer value return a\n\
-  Python integer (``long`` on Python 2, ``int`` on Python 3).\n\
-- Options documented by libcurl to return a floating point value\n\
-  return a Python ``float``.\n\
-- Options documented by libcurl to return a string value\n\
-  return a Python string (``str`` on Python 2 and Python 3).\n\
-  On Python 2, the string contains whatever data libcurl returned.\n\
-  On Python 3, the data returned by libcurl is decoded using the\n\
-  default string encoding at the time of the call.\n\
-  If the data cannot be decoded using the default encoding, ``UnicodeDecodeError``\n\
-  is raised. Use :ref:`getinfo_raw <getinfo_raw>`\n\
-  to retrieve the data as ``bytes`` in these\n\
-  cases.\n\
-- ``SSL_ENGINES`` and ``INFO_COOKIELIST`` return a list of strings.\n\
-  The same encoding caveats apply; use :ref:`getinfo_raw <getinfo_raw>`\n\
-  to retrieve the\n\
-  data as a list of byte strings.\n\
-- ``INFO_CERTINFO`` returns a list with one element\n\
-  per certificate in the chain, starting with the leaf; each element is a\n\
-  sequence of *(key, value)* tuples where both ``key`` and ``value`` are\n\
-  strings. String encoding caveats apply; use :ref:`getinfo_raw <getinfo_raw>`\n\
-  to retrieve\n\
-  certificate data as byte strings.\n\
-\n\
-On Python 2, ``getinfo`` and ``getinfo_raw`` behave identically.\n\
-\n\
-Example usage::\n\
-\n\
-    import pycurl\n\
-    c = pycurl.Curl()\n\
-    c.setopt(pycurl.OPT_CERTINFO, 1)\n\
-    c.setopt(pycurl.URL, \"https://python.org\")\n\
-    c.setopt(pycurl.FOLLOWLOCATION, 1)\n\
-    c.perform()\n\
-    print(c.getinfo(pycurl.HTTP_CODE))\n\
-    # --> 200\n\
-    print(c.getinfo(pycurl.EFFECTIVE_URL))\n\
-    # --> \"https://www.python.org/\"\n\
-    certinfo = c.getinfo(pycurl.INFO_CERTINFO)\n\
-    print(certinfo)\n\
-    # --> [(('Subject', 'C = AU, ST = Some-State, O = PycURL test suite,\n\
-             CN = localhost'), ('Issuer', 'C = AU, ST = Some-State,\n\
-             O = PycURL test suite, OU = localhost, CN = localhost'),\n\
-            ('Version', '0'), ...)]\n\
-\n\
-\n\
-Raises pycurl.error exception upon failure.\n\
-\n\
-.. _curl_easy_getinfo:\n\
-    https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html";
-
-PYCURL_INTERNAL const char curl_getinfo_raw_doc[] = "getinfo_raw(option) -> Result\n\
-\n\
-Extract and return information from a curl session,\n\
-returning string data as byte strings.\n\
-Corresponds to `curl_easy_getinfo`_ in libcurl.\n\
-The ``getinfo_raw`` method should not be called unless\n\
-``perform`` has been called and finished.\n\
-\n\
-*option* is a constant corresponding to one of the\n\
-``CURLINFO_*`` constants in libcurl. Most option constant names match\n\
-the respective ``CURLINFO_*`` constant names with the ``CURLINFO_`` prefix\n\
-removed, for example ``CURLINFO_CONTENT_TYPE`` is accessible as\n\
-``pycurl.CONTENT_TYPE``. Exceptions to this rule are as follows:\n\
-\n\
-- ``CURLINFO_FILETIME`` is mapped as ``pycurl.INFO_FILETIME``\n\
-- ``CURLINFO_COOKIELIST`` is mapped as ``pycurl.INFO_COOKIELIST``\n\
-- ``CURLINFO_CERTINFO`` is mapped as ``pycurl.INFO_CERTINFO``\n\
-- ``CURLINFO_RTSP_CLIENT_CSEQ`` is mapped as ``pycurl.INFO_RTSP_CLIENT_CSEQ``\n\
-- ``CURLINFO_RTSP_CSEQ_RECV`` is mapped as ``pycurl.INFO_RTSP_CSEQ_RECV``\n\
-- ``CURLINFO_RTSP_SERVER_CSEQ`` is mapped as ``pycurl.INFO_RTSP_SERVER_CSEQ``\n\
-- ``CURLINFO_RTSP_SESSION_ID`` is mapped as ``pycurl.INFO_RTSP_SESSION_ID``\n\
-\n\
-The type of return value depends on the option, as follows:\n\
-\n\
-- Options documented by libcurl to return an integer value return a\n\
-  Python integer (``long`` on Python 2, ``int`` on Python 3).\n\
-- Options documented by libcurl to return a floating point value\n\
-  return a Python ``float``.\n\
-- Options documented by libcurl to return a string value\n\
-  return a Python byte string (``str`` on Python 2, ``bytes`` on Python 3).\n\
-  The string contains whatever data libcurl returned.\n\
-  Use :ref:`getinfo <getinfo>` to retrieve this data as a Unicode string on Python 3.\n\
-- ``SSL_ENGINES`` and ``INFO_COOKIELIST`` return a list of byte strings.\n\
-  The same encoding caveats apply; use :ref:`getinfo <getinfo>` to retrieve the\n\
-  data as a list of potentially Unicode strings.\n\
-- ``INFO_CERTINFO`` returns a list with one element\n\
-  per certificate in the chain, starting with the leaf; each element is a\n\
-  sequence of *(key, value)* tuples where both ``key`` and ``value`` are\n\
-  byte strings. String encoding caveats apply; use :ref:`getinfo <getinfo>`\n\
-  to retrieve\n\
-  certificate data as potentially Unicode strings.\n\
-\n\
-On Python 2, ``getinfo`` and ``getinfo_raw`` behave identically.\n\
-\n\
-Example usage::\n\
-\n\
-    import pycurl\n\
-    c = pycurl.Curl()\n\
-    c.setopt(pycurl.OPT_CERTINFO, 1)\n\
-    c.setopt(pycurl.URL, \"https://python.org\")\n\
-    c.setopt(pycurl.FOLLOWLOCATION, 1)\n\
-    c.perform()\n\
-    print(c.getinfo_raw(pycurl.HTTP_CODE))\n\
-    # --> 200\n\
-    print(c.getinfo_raw(pycurl.EFFECTIVE_URL))\n\
-    # --> b\"https://www.python.org/\"\n\
-    certinfo = c.getinfo_raw(pycurl.INFO_CERTINFO)\n\
-    print(certinfo)\n\
-    # --> [((b'Subject', b'C = AU, ST = Some-State, O = PycURL test suite,\n\
-             CN = localhost'), (b'Issuer', b'C = AU, ST = Some-State,\n\
-             O = PycURL test suite, OU = localhost, CN = localhost'),\n\
-            (b'Version', b'0'), ...)]\n\
-\n\
-\n\
-Raises pycurl.error exception upon failure.\n\
-\n\
-*Added in version 7.43.0.2.*\n\
-\n\
-.. _curl_easy_getinfo:\n\
-    https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html";
-
-PYCURL_INTERNAL const char curl_pause_doc[] = "pause(bitmask) -> None\n\
-\n\
-Pause or unpause a curl handle. Bitmask should be a value such as\n\
-PAUSE_RECV or PAUSE_CONT.\n\
-\n\
-Corresponds to `curl_easy_pause`_ in libcurl. The argument should be\n\
-derived from the ``PAUSE_RECV``, ``PAUSE_SEND``, ``PAUSE_ALL`` and\n\
-``PAUSE_CONT`` constants.\n\
-\n\
-Raises pycurl.error exception upon failure.\n\
-\n\
-.. _curl_easy_pause: https://curl.haxx.se/libcurl/c/curl_easy_pause.html";
-
-PYCURL_INTERNAL const char curl_perform_doc[] = "perform() -> None\n\
-\n\
-Perform a file transfer.\n\
-\n\
-Corresponds to `curl_easy_perform`_ in libcurl.\n\
-\n\
-Raises pycurl.error exception upon failure.\n\
-\n\
-.. _curl_easy_perform:\n\
-    https://curl.haxx.se/libcurl/c/curl_easy_perform.html";
-
-PYCURL_INTERNAL const char curl_perform_rb_doc[] = "perform_rb() -> response_body\n\
-\n\
-Perform a file transfer and return response body as a byte string.\n\
-\n\
-This method arranges for response body to be saved in a StringIO\n\
-(Python 2) or BytesIO (Python 3) instance, then invokes :ref:`perform <perform>`\n\
-to perform the file transfer, then returns the value of the StringIO/BytesIO\n\
-instance which is a ``str`` instance on Python 2 and ``bytes`` instance\n\
-on Python 3. Errors during transfer raise ``pycurl.error`` exceptions\n\
-just like in :ref:`perform <perform>`.\n\
-\n\
-Use :ref:`perform_rs <perform_rs>` to retrieve response body as a string\n\
-(``str`` instance on both Python 2 and 3).\n\
-\n\
-Raises ``pycurl.error`` exception upon failure.\n\
-\n\
-*Added in version 7.43.0.2.*";
-
-PYCURL_INTERNAL const char curl_perform_rs_doc[] = "perform_rs() -> response_body\n\
-\n\
-Perform a file transfer and return response body as a string.\n\
-\n\
-On Python 2, this method arranges for response body to be saved in a StringIO\n\
-instance, then invokes :ref:`perform <perform>`\n\
-to perform the file transfer, then returns the value of the StringIO instance.\n\
-This behavior is identical to :ref:`perform_rb <perform_rb>`.\n\
-\n\
-On Python 3, this method arranges for response body to be saved in a BytesIO\n\
-instance, then invokes :ref:`perform <perform>`\n\
-to perform the file transfer, then decodes the response body in Python's\n\
-default encoding and returns the decoded body as a Unicode string\n\
-(``str`` instance). *Note:* decoding happens after the transfer finishes,\n\
-thus an encoding error implies the transfer/network operation succeeded.\n\
-\n\
-Any transfer errors raise ``pycurl.error`` exception,\n\
-just like in :ref:`perform <perform>`.\n\
-\n\
-Use :ref:`perform_rb <perform_rb>` to retrieve response body as a byte\n\
-string (``bytes`` instance on Python 3) without attempting to decode it.\n\
-\n\
-Raises ``pycurl.error`` exception upon failure.\n\
-\n\
-*Added in version 7.43.0.2.*";
-
-PYCURL_INTERNAL const char curl_reset_doc[] = "reset() -> None\n\
-\n\
-Reset all options set on curl handle to default values, but preserves\n\
-live connections, session ID cache, DNS cache, cookies, and shares.\n\
-\n\
-Corresponds to `curl_easy_reset`_ in libcurl.\n\
-\n\
-.. _curl_easy_reset: https://curl.haxx.se/libcurl/c/curl_easy_reset.html";
-
-PYCURL_INTERNAL const char curl_set_ca_certs_doc[] = "set_ca_certs() -> None\n\
-\n\
-Load ca certs from provided unicode string.\n\
-\n\
-Note that certificates will be added only when cURL starts new connection.";
-
-PYCURL_INTERNAL const char curl_setopt_doc[] = "setopt(option, value) -> None\n\
-\n\
-Set curl session option. Corresponds to `curl_easy_setopt`_ in libcurl.\n\
-\n\
-*option* specifies which option to set. PycURL defines constants\n\
-corresponding to ``CURLOPT_*`` constants in libcurl, except that\n\
-the ``CURLOPT_`` prefix is removed. For example, ``CURLOPT_URL`` is\n\
-exposed in PycURL as ``pycurl.URL``, with some exceptions as detailed below.\n\
-For convenience, ``CURLOPT_*``\n\
-constants are also exposed on the Curl objects themselves::\n\
-\n\
-    import pycurl\n\
-    c = pycurl.Curl()\n\
-    c.setopt(pycurl.URL, \"http://www.python.org/\")\n\
-    # Same as:\n\
-    c.setopt(c.URL, \"http://www.python.org/\")\n\
-\n\
-The following are exceptions to option constant naming convention:\n\
-\n\
-- ``CURLOPT_FILETIME`` is mapped as ``pycurl.OPT_FILETIME``\n\
-- ``CURLOPT_CERTINFO`` is mapped as ``pycurl.OPT_CERTINFO``\n\
-- ``CURLOPT_COOKIELIST`` is mapped as ``pycurl.COOKIELIST``\n\
-  and, as of PycURL 7.43.0.2, also as ``pycurl.OPT_COOKIELIST``\n\
-- ``CURLOPT_RTSP_CLIENT_CSEQ`` is mapped as ``pycurl.OPT_RTSP_CLIENT_CSEQ``\n\
-- ``CURLOPT_RTSP_REQUEST`` is mapped as ``pycurl.OPT_RTSP_REQUEST``\n\
-- ``CURLOPT_RTSP_SERVER_CSEQ`` is mapped as ``pycurl.OPT_RTSP_SERVER_CSEQ``\n\
-- ``CURLOPT_RTSP_SESSION_ID`` is mapped as ``pycurl.OPT_RTSP_SESSION_ID``\n\
-- ``CURLOPT_RTSP_STREAM_URI`` is mapped as ``pycurl.OPT_RTSP_STREAM_URI``\n\
-- ``CURLOPT_RTSP_TRANSPORT`` is mapped as ``pycurl.OPT_RTSP_TRANSPORT``\n\
-\n\
-*value* specifies the value to set the option to. Different options accept\n\
-values of different types:\n\
-\n\
-- Options specified by `curl_easy_setopt`_ as accepting ``1`` or an\n\
-  integer value accept Python integers, long integers (on Python 2.x) and\n\
-  booleans::\n\
-\n\
-    c.setopt(pycurl.FOLLOWLOCATION, True)\n\
-    c.setopt(pycurl.FOLLOWLOCATION, 1)\n\
-    # Python 2.x only:\n\
-    c.setopt(pycurl.FOLLOWLOCATION, 1L)\n\
-\n\
-- Options specified as accepting strings by ``curl_easy_setopt`` accept\n\
-  byte strings (``str`` on Python 2, ``bytes`` on Python 3) and\n\
-  Unicode strings with ASCII code points only.\n\
-  For more information, please refer to :ref:`unicode`. Example::\n\
-\n\
-    c.setopt(pycurl.URL, \"http://www.python.org/\")\n\
-    c.setopt(pycurl.URL, u\"http://www.python.org/\")\n\
-    # Python 3.x only:\n\
-    c.setopt(pycurl.URL, b\"http://www.python.org/\")\n\
-\n\
-- ``HTTP200ALIASES``, ``HTTPHEADER``, ``POSTQUOTE``, ``PREQUOTE``,\n\
-  ``PROXYHEADER`` and\n\
-  ``QUOTE`` accept a list or tuple of strings. The same rules apply to these\n\
-  strings as do to string option values. Example::\n\
-\n\
-    c.setopt(pycurl.HTTPHEADER, [\"Accept:\"])\n\
-    c.setopt(pycurl.HTTPHEADER, (\"Accept:\",))\n\
-\n\
-- ``READDATA`` accepts a file object or any Python object which has\n\
-  a ``read`` method. On Python 2, a file object will be passed directly\n\
-  to libcurl and may result in greater transfer efficiency, unless\n\
-  PycURL has been compiled with ``AVOID_STDIO`` option.\n\
-  On Python 3 and on Python 2 when the value is not a true file object,\n\
-  ``READDATA`` is emulated in PycURL via ``READFUNCTION``.\n\
-  The file should generally be opened in binary mode. Example::\n\
-\n\
-    f = open('file.txt', 'rb')\n\
-    c.setopt(c.READDATA, f)\n\
-\n\
-- ``WRITEDATA`` and ``WRITEHEADER`` accept a file object or any Python\n\
-  object which has a ``write`` method. On Python 2, a file object will\n\
-  be passed directly to libcurl and may result in greater transfer efficiency,\n\
-  unless PycURL has been compiled with ``AVOID_STDIO`` option.\n\
-  On Python 3 and on Python 2 when the value is not a true file object,\n\
-  ``WRITEDATA`` is emulated in PycURL via ``WRITEFUNCTION``.\n\
-  The file should generally be opened in binary mode. Example::\n\
-\n\
-    f = open('/dev/null', 'wb')\n\
-    c.setopt(c.WRITEDATA, f)\n\
-\n\
-- ``*FUNCTION`` options accept a function. Supported callbacks are documented\n\
-  in :ref:`callbacks`. Example::\n\
-\n\
-    # Python 2\n\
-    import StringIO\n\
-    b = StringIO.StringIO()\n\
-    c.setopt(pycurl.WRITEFUNCTION, b.write)\n\
-\n\
-- ``SHARE`` option accepts a :ref:`curlshareobject`.\n\
-\n\
-It is possible to set integer options - and only them - that PycURL does\n\
-not know about by using the numeric value of the option constant directly.\n\
-For example, ``pycurl.VERBOSE`` has the value 42, and may be set as follows::\n\
-\n\
-    c.setopt(42, 1)\n\
-\n\
-*setopt* can reset some options to their default value, performing the job of\n\
-:py:meth:`pycurl.Curl.unsetopt`, if ``None`` is passed\n\
-for the option value. The following two calls are equivalent::\n\
-\n\
-    c.setopt(c.URL, None)\n\
-    c.unsetopt(c.URL)\n\
-\n\
-Raises TypeError when the option value is not of a type accepted by the\n\
-respective option, and pycurl.error exception when libcurl rejects the\n\
-option or its value.\n\
-\n\
-.. _curl_easy_setopt: https://curl.haxx.se/libcurl/c/curl_easy_setopt.html";
-
-PYCURL_INTERNAL const char curl_setopt_string_doc[] = "setopt_string(option, value) -> None\n\
-\n\
-Set curl session option to a string value.\n\
-\n\
-This method allows setting string options that are not officially supported\n\
-by PycURL, for example because they did not exist when the version of PycURL\n\
-being used was released.\n\
-:py:meth:`pycurl.Curl.setopt` should be used for setting options that\n\
-PycURL knows about.\n\
-\n\
-**Warning:** No checking is performed that *option* does, in fact,\n\
-expect a string value. Using this method incorrectly can crash the program\n\
-and may lead to a security vulnerability.\n\
-Furthermore, it is on the application to ensure that the *value* object\n\
-does not get garbage collected while libcurl is using it.\n\
-libcurl copies most string options but not all; one option whose value\n\
-is not copied by libcurl is `CURLOPT_POSTFIELDS`_.\n\
-\n\
-*option* would generally need to be given as an integer literal rather than\n\
-a symbolic constant.\n\
-\n\
-*value* can be a binary string or a Unicode string using ASCII code points,\n\
-same as with string options given to PycURL elsewhere.\n\
-\n\
-Example setting URL via ``setopt_string``::\n\
-\n\
-    import pycurl\n\
-    c = pycurl.Curl()\n\
-    c.setopt_string(10002, \"http://www.python.org/\")\n\
-\n\
-.. _CURLOPT_POSTFIELDS: https://curl.haxx.se/libcurl/c/CURLOPT_POSTFIELDS.html";
-
-PYCURL_INTERNAL const char curl_unsetopt_doc[] = "unsetopt(option) -> None\n\
-\n\
-Reset curl session option to its default value.\n\
-\n\
-Only some curl options may be reset via this method.\n\
-\n\
-libcurl does not provide a general way to reset a single option to its default value;\n\
-:py:meth:`pycurl.Curl.reset` resets all options to their default values,\n\
-otherwise :py:meth:`pycurl.Curl.setopt` must be called with whatever value\n\
-is the default. For convenience, PycURL provides this unsetopt method\n\
-to reset some of the options to their default values.\n\
-\n\
-Raises pycurl.error exception on failure.\n\
-\n\
-``c.unsetopt(option)`` is equivalent to ``c.setopt(option, None)``.";
-
-PYCURL_INTERNAL const char multi_doc[] = "CurlMulti() -> New CurlMulti object\n\
-\n\
-Creates a new :ref:`curlmultiobject` which corresponds to\n\
-a ``CURLM`` handle in libcurl.";
-
-PYCURL_INTERNAL const char multi_add_handle_doc[] = "add_handle(Curl object) -> None\n\
-\n\
-Corresponds to `curl_multi_add_handle`_ in libcurl. This method adds an\n\
-existing and valid Curl object to the CurlMulti object.\n\
-\n\
-*Changed in version 7.43.0.2:* add_handle now ensures that the Curl object\n\
-is not garbage collected while it is being used by a CurlMulti object.\n\
-Previously application had to maintain an outstanding reference to the Curl\n\
-object to keep it from being garbage collected.\n\
-\n\
-.. _curl_multi_add_handle:\n\
-    https://curl.haxx.se/libcurl/c/curl_multi_add_handle.html";
-
-PYCURL_INTERNAL const char multi_assign_doc[] = "assign(sock_fd, object) -> None\n\
-\n\
-Creates an association in the multi handle between the given socket and\n\
-a private object in the application.\n\
-Corresponds to `curl_multi_assign`_ in libcurl.\n\
-\n\
-.. _curl_multi_assign: https://curl.haxx.se/libcurl/c/curl_multi_assign.html";
-
-PYCURL_INTERNAL const char multi_close_doc[] = "close() -> None\n\
-\n\
-Corresponds to `curl_multi_cleanup`_ in libcurl. This method is\n\
-automatically called by pycurl when a CurlMulti object no longer has any\n\
-references to it, but can also be called explicitly.\n\
-\n\
-.. _curl_multi_cleanup:\n\
-    https://curl.haxx.se/libcurl/c/curl_multi_cleanup.html";
-
-PYCURL_INTERNAL const char multi_fdset_doc[] = "fdset() -> tuple of lists with active file descriptors, readable, writeable, exceptions\n\
-\n\
-Returns a tuple of three lists that can be passed to the select.select() method.\n\
-\n\
-Corresponds to `curl_multi_fdset`_ in libcurl. This method extracts the\n\
-file descriptor information from a CurlMulti object. The returned lists can\n\
-be used with the ``select`` module to poll for events.\n\
-\n\
-Example usage::\n\
-\n\
-    import pycurl\n\
-    c = pycurl.Curl()\n\
-    c.setopt(pycurl.URL, \"https://curl.haxx.se\")\n\
-    m = pycurl.CurlMulti()\n\
-    m.add_handle(c)\n\
-    while 1:\n\
-        ret, num_handles = m.perform()\n\
-        if ret != pycurl.E_CALL_MULTI_PERFORM: break\n\
-    while num_handles:\n\
-        apply(select.select, m.fdset() + (1,))\n\
-        while 1:\n\
-            ret, num_handles = m.perform()\n\
-            if ret != pycurl.E_CALL_MULTI_PERFORM: break\n\
-\n\
-.. _curl_multi_fdset:\n\
-    https://curl.haxx.se/libcurl/c/curl_multi_fdset.html";
-
-PYCURL_INTERNAL const char multi_info_read_doc[] = "info_read([max_objects]) -> tuple(number of queued messages, a list of successful objects, a list of failed objects)\n\
-\n\
-Corresponds to the `curl_multi_info_read`_ function in libcurl.\n\
-\n\
-This method extracts at most *max* messages from the multi stack and returns\n\
-them in two lists. The first list contains the handles which completed\n\
-successfully and the second list contains a tuple *(curl object, curl error\n\
-number, curl error message)* for each failed curl object. The curl error\n\
-message is returned as a Python string which is decoded from the curl error\n\
-string using the `surrogateescape`_ error handler. The number of\n\
-queued messages after this method has been called is also returned.\n\
-\n\
-.. _curl_multi_info_read:\n\
-    https://curl.haxx.se/libcurl/c/curl_multi_info_read.html\n\
-\n\
-.. _surrogateescape:\n\
-    https://www.python.org/dev/peps/pep-0383/";
-
-PYCURL_INTERNAL const char multi_perform_doc[] = "perform() -> tuple of status and the number of active Curl objects\n\
-\n\
-Corresponds to `curl_multi_perform`_ in libcurl.\n\
-\n\
-.. _curl_multi_perform:\n\
-    https://curl.haxx.se/libcurl/c/curl_multi_perform.html";
-
-PYCURL_INTERNAL const char multi_remove_handle_doc[] = "remove_handle(Curl object) -> None\n\
-\n\
-Corresponds to `curl_multi_remove_handle`_ in libcurl. This method\n\
-removes an existing and valid Curl object from the CurlMulti object.\n\
-\n\
-.. _curl_multi_remove_handle:\n\
-    https://curl.haxx.se/libcurl/c/curl_multi_remove_handle.html";
-
-PYCURL_INTERNAL const char multi_select_doc[] = "select([timeout]) -> number of ready file descriptors or 0 on timeout\n\
-\n\
-Returns result from doing a select() on the curl multi file descriptor\n\
-with the given timeout.\n\
-\n\
-This is a convenience function which simplifies the combined use of\n\
-``fdset()`` and the ``select`` module.\n\
-\n\
-Example usage::\n\
-\n\
-    import pycurl\n\
-    c = pycurl.Curl()\n\
-    c.setopt(pycurl.URL, \"https://curl.haxx.se\")\n\
-    m = pycurl.CurlMulti()\n\
-    m.add_handle(c)\n\
-    while 1:\n\
-        ret, num_handles = m.perform()\n\
-        if ret != pycurl.E_CALL_MULTI_PERFORM: break\n\
-    while num_handles:\n\
-        ret = m.select(1.0)\n\
-        if ret == 0:  continue\n\
-        while 1:\n\
-            ret, num_handles = m.perform()\n\
-            if ret != pycurl.E_CALL_MULTI_PERFORM: break";
-
-PYCURL_INTERNAL const char multi_setopt_doc[] = "setopt(option, value) -> None\n\
-\n\
-Set curl multi option. Corresponds to `curl_multi_setopt`_ in libcurl.\n\
-\n\
-*option* specifies which option to set. PycURL defines constants\n\
-corresponding to ``CURLMOPT_*`` constants in libcurl, except that\n\
-the ``CURLMOPT_`` prefix is replaced with ``M_`` prefix.\n\
-For example, ``CURLMOPT_PIPELINING`` is\n\
-exposed in PycURL as ``pycurl.M_PIPELINING``. For convenience, ``CURLMOPT_*``\n\
-constants are also exposed on CurlMulti objects::\n\
-\n\
-    import pycurl\n\
-    m = pycurl.CurlMulti()\n\
-    m.setopt(pycurl.M_PIPELINING, 1)\n\
-    # Same as:\n\
-    m.setopt(m.M_PIPELINING, 1)\n\
-\n\
-*value* specifies the value to set the option to. Different options accept\n\
-values of different types:\n\
-\n\
-- Options specified by `curl_multi_setopt`_ as accepting ``1`` or an\n\
-  integer value accept Python integers, long integers (on Python 2.x) and\n\
-  booleans::\n\
-\n\
-    m.setopt(pycurl.M_PIPELINING, True)\n\
-    m.setopt(pycurl.M_PIPELINING, 1)\n\
-    # Python 2.x only:\n\
-    m.setopt(pycurl.M_PIPELINING, 1L)\n\
-\n\
-- ``*FUNCTION`` options accept a function. Supported callbacks are\n\
-  ``CURLMOPT_SOCKETFUNCTION`` AND ``CURLMOPT_TIMERFUNCTION``. Please refer to\n\
-  the PycURL test suite for examples on using the callbacks.\n\
-\n\
-Raises TypeError when the option value is not of a type accepted by the\n\
-respective option, and pycurl.error exception when libcurl rejects the\n\
-option or its value.\n\
-\n\
-.. _curl_multi_setopt: https://curl.haxx.se/libcurl/c/curl_multi_setopt.html";
-
-PYCURL_INTERNAL const char multi_socket_action_doc[] = "socket_action(sock_fd, ev_bitmask) -> (result, num_running_handles)\n\
-\n\
-Returns result from doing a socket_action() on the curl multi file descriptor\n\
-with the given timeout.\n\
-Corresponds to `curl_multi_socket_action`_ in libcurl.\n\
-\n\
-The return value is a two-element tuple. The first element is the return\n\
-value of the underlying ``curl_multi_socket_action`` function, and it is\n\
-always zero (``CURLE_OK``) because any other return value would cause\n\
-``socket_action`` to raise an exception. The second element is the number of\n\
-running easy handles within this multi handle. When the number of running\n\
-handles reaches zero, all transfers have completed. Note that if the number\n\
-of running handles has decreased by one compared to the previous invocation,\n\
-this is not mean the handle corresponding to the ``sock_fd`` provided as\n\
-the argument to this function was the completed handle.\n\
-\n\
-.. _curl_multi_socket_action: https://curl.haxx.se/libcurl/c/curl_multi_socket_action.html";
-
-PYCURL_INTERNAL const char multi_socket_all_doc[] = "socket_all() -> tuple\n\
-\n\
-Returns result from doing a socket_all() on the curl multi file descriptor\n\
-with the given timeout.";
-
-PYCURL_INTERNAL const char multi_timeout_doc[] = "timeout() -> int\n\
-\n\
-Returns how long to wait for action before proceeding.\n\
-Corresponds to `curl_multi_timeout`_ in libcurl.\n\
-\n\
-.. _curl_multi_timeout: https://curl.haxx.se/libcurl/c/curl_multi_timeout.html";
-
-PYCURL_INTERNAL const char pycurl_global_cleanup_doc[] = "global_cleanup() -> None\n\
-\n\
-Cleanup curl environment.\n\
-\n\
-Corresponds to `curl_global_cleanup`_ in libcurl.\n\
-\n\
-.. _curl_global_cleanup: https://curl.haxx.se/libcurl/c/curl_global_cleanup.html";
-
-PYCURL_INTERNAL const char pycurl_global_init_doc[] = "global_init(option) -> None\n\
-\n\
-Initialize curl environment.\n\
-\n\
-*option* is one of the constants pycurl.GLOBAL_SSL, pycurl.GLOBAL_WIN32,\n\
-pycurl.GLOBAL_ALL, pycurl.GLOBAL_NOTHING, pycurl.GLOBAL_DEFAULT.\n\
-\n\
-Corresponds to `curl_global_init`_ in libcurl.\n\
-\n\
-.. _curl_global_init: https://curl.haxx.se/libcurl/c/curl_global_init.html";
-
-PYCURL_INTERNAL const char pycurl_module_doc[] = "This module implements an interface to the cURL library.\n\
-\n\
-Types:\n\
-\n\
-Curl() -> New object.  Create a new curl object.\n\
-CurlMulti() -> New object.  Create a new curl multi object.\n\
-CurlShare() -> New object.  Create a new curl share object.\n\
-\n\
-Functions:\n\
-\n\
-global_init(option) -> None.  Initialize curl environment.\n\
-global_cleanup() -> None.  Cleanup curl environment.\n\
-version_info() -> tuple.  Return version information.";
-
-PYCURL_INTERNAL const char pycurl_version_info_doc[] = "version_info() -> tuple\n\
-\n\
-Returns a 12-tuple with the version info.\n\
-\n\
-Corresponds to `curl_version_info`_ in libcurl. Returns a tuple of\n\
-information which is similar to the ``curl_version_info_data`` struct\n\
-returned by ``curl_version_info()`` in libcurl.\n\
-\n\
-Example usage::\n\
-\n\
-    >>> import pycurl\n\
-    >>> pycurl.version_info()\n\
-    (3, '7.33.0', 467200, 'amd64-portbld-freebsd9.1', 33436, 'OpenSSL/0.9.8x',\n\
-    0, '1.2.7', ('dict', 'file', 'ftp', 'ftps', 'gopher', 'http', 'https',\n\
-    'imap', 'imaps', 'pop3', 'pop3s', 'rtsp', 'smtp', 'smtps', 'telnet',\n\
-    'tftp'), None, 0, None)\n\
-\n\
-.. _curl_version_info: https://curl.haxx.se/libcurl/c/curl_version_info.html";
-
-PYCURL_INTERNAL const char share_doc[] = "CurlShare() -> New CurlShare object\n\
-\n\
-Creates a new :ref:`curlshareobject` which corresponds to a\n\
-``CURLSH`` handle in libcurl. CurlShare objects is what you pass as an\n\
-argument to the SHARE option on :ref:`Curl objects <curlobject>`.";
-
-PYCURL_INTERNAL const char share_close_doc[] = "close() -> None\n\
-\n\
-Close shared handle.\n\
-\n\
-Corresponds to `curl_share_cleanup`_ in libcurl. This method is\n\
-automatically called by pycurl when a CurlShare object no longer has\n\
-any references to it, but can also be called explicitly.\n\
-\n\
-.. _curl_share_cleanup:\n\
-    https://curl.haxx.se/libcurl/c/curl_share_cleanup.html";
-
-PYCURL_INTERNAL const char share_setopt_doc[] = "setopt(option, value) -> None\n\
-\n\
-Set curl share option.\n\
-\n\
-Corresponds to `curl_share_setopt`_ in libcurl, where *option* is\n\
-specified with the ``CURLSHOPT_*`` constants in libcurl, except that the\n\
-``CURLSHOPT_`` prefix has been changed to ``SH_``. Currently, *value* must be\n\
-one of: ``LOCK_DATA_COOKIE``, ``LOCK_DATA_DNS``, ``LOCK_DATA_SSL_SESSION`` or\n\
-``LOCK_DATA_CONNECT``.\n\
-\n\
-Example usage::\n\
-\n\
-    import pycurl\n\
-    curl = pycurl.Curl()\n\
-    s = pycurl.CurlShare()\n\
-    s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE)\n\
-    s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS)\n\
-    curl.setopt(pycurl.URL, 'https://curl.haxx.se')\n\
-    curl.setopt(pycurl.SHARE, s)\n\
-    curl.perform()\n\
-    curl.close()\n\
-\n\
-Raises pycurl.error exception upon failure.\n\
-\n\
-.. _curl_share_setopt:\n\
-    https://curl.haxx.se/libcurl/c/curl_share_setopt.html";
-
diff --git a/src/docstrings.h b/src/docstrings.h
deleted file mode 100644 (file)
index 1932120..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Generated file - do not edit. */
-/* See doc/docstrings/ *.rst. */
-
-extern const char curl_doc[];
-extern const char curl_close_doc[];
-extern const char curl_duphandle_doc[];
-extern const char curl_errstr_doc[];
-extern const char curl_errstr_raw_doc[];
-extern const char curl_getinfo_doc[];
-extern const char curl_getinfo_raw_doc[];
-extern const char curl_pause_doc[];
-extern const char curl_perform_doc[];
-extern const char curl_perform_rb_doc[];
-extern const char curl_perform_rs_doc[];
-extern const char curl_reset_doc[];
-extern const char curl_set_ca_certs_doc[];
-extern const char curl_setopt_doc[];
-extern const char curl_setopt_string_doc[];
-extern const char curl_unsetopt_doc[];
-extern const char multi_doc[];
-extern const char multi_add_handle_doc[];
-extern const char multi_assign_doc[];
-extern const char multi_close_doc[];
-extern const char multi_fdset_doc[];
-extern const char multi_info_read_doc[];
-extern const char multi_perform_doc[];
-extern const char multi_remove_handle_doc[];
-extern const char multi_select_doc[];
-extern const char multi_setopt_doc[];
-extern const char multi_socket_action_doc[];
-extern const char multi_socket_all_doc[];
-extern const char multi_timeout_doc[];
-extern const char pycurl_global_cleanup_doc[];
-extern const char pycurl_global_init_doc[];
-extern const char pycurl_module_doc[];
-extern const char pycurl_version_info_doc[];
-extern const char share_doc[];
-extern const char share_close_doc[];
-extern const char share_setopt_doc[];
index 1b3464a71c7149e55bb4dc0185d0c3f73f125174..a5f7849fabd4e43c59a057fcb6533dc67765ff76 100644 (file)
@@ -302,7 +302,7 @@ error:
 
 /* duphandle */
 PYCURL_INTERNAL CurlObject *
-do_curl_duphandle(CurlObject *self)
+do_curl_duphandle(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyTypeObject *subtype;
     CurlObject *dup;
@@ -617,7 +617,7 @@ do_curl_dealloc(CurlObject *self)
 
 
 static PyObject *
-do_curl_close(CurlObject *self)
+do_curl_close(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     if (check_curl_state(self, 2, "close") != 0) {
         return NULL;
@@ -706,7 +706,7 @@ do_curl_traverse(CurlObject *self, visitproc visit, void *arg)
 /* ------------------------ reset ------------------------ */
 
 static PyObject*
-do_curl_reset(CurlObject *self)
+do_curl_reset(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     int res;
 
@@ -726,7 +726,7 @@ do_curl_reset(CurlObject *self)
 }
 
 
-static PyObject *do_curl_getstate(CurlObject *self)
+static PyObject *do_curl_getstate(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyErr_SetString(PyExc_TypeError, "Curl objects do not support serialization");
     return NULL;
index 16668141b62621d6fd9eb452535d6cee11a37fef..2e2a711bf3e1251d7ac436886f4ffeb31f73018f 100644 (file)
@@ -356,7 +356,7 @@ do_curl_getinfo(CurlObject *self, PyObject *args)
 
 
 PYCURL_INTERNAL PyObject *
-do_curl_errstr(CurlObject *self)
+do_curl_errstr(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     if (check_curl_state(self, 1 | 2, "errstr") != 0) {
         return NULL;
@@ -369,7 +369,7 @@ do_curl_errstr(CurlObject *self)
 
 #if PY_MAJOR_VERSION >= 3
 PYCURL_INTERNAL PyObject *
-do_curl_errstr_raw(CurlObject *self)
+do_curl_errstr_raw(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     if (check_curl_state(self, 1 | 2, "errstr") != 0) {
         return NULL;
index faa064957bc2e8340a245e81a10fc31b3de96906..d209aaa719e0a3fb8186cc3540b0b857133fe8e1 100644 (file)
@@ -79,7 +79,15 @@ util_curl_unsetopt(CurlObject *self, int option)
         Py_CLEAR(self->writeheader_fp);
         break;
     case CURLOPT_CAINFO:
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 77, 0)
+    case CURLOPT_CAINFO_BLOB:
+#endif
     case CURLOPT_CAPATH:
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    case CURLOPT_SSLCERT_BLOB:
+    case CURLOPT_SSLKEY_BLOB:
+    case CURLOPT_ISSUERCERT_BLOB:
+#endif
     case CURLOPT_COOKIE:
     case CURLOPT_COOKIEJAR:
     case CURLOPT_CUSTOMREQUEST:
@@ -115,6 +123,15 @@ util_curl_unsetopt(CurlObject *self, int option)
     case CURLOPT_PROXY_SSLCERTTYPE:
     case CURLOPT_PROXY_SSLKEY:
     case CURLOPT_PROXY_SSLKEYTYPE:
+    case CURLOPT_PROXY_SSL_CIPHER_LIST:
+#endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 77, 0)
+    case CURLOPT_PROXY_CAINFO_BLOB:
+#endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    case CURLOPT_PROXY_SSLCERT_BLOB:
+    case CURLOPT_PROXY_SSLKEY_BLOB:
+    case CURLOPT_PROXY_ISSUERCERT_BLOB:
 #endif
         SETOPT((char *) NULL);
         break;
@@ -188,8 +205,13 @@ do_curl_unsetopt(CurlObject *self, PyObject *args)
     /* early checks of option value */
     if (option <= 0)
         goto error;
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    if (option >= (int)CURLOPTTYPE_BLOB + OPTIONS_SIZE)
+        goto error;
+#else
     if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE)
         goto error;
+#endif
     if (option % 10000 >= OPTIONS_SIZE)
         goto error;
 
@@ -207,6 +229,9 @@ do_curl_setopt_string_impl(CurlObject *self, int option, PyObject *obj)
     char *str = NULL;
     Py_ssize_t len = -1;
     PyObject *encoded_obj;
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    struct curl_blob curlblob;
+#endif
     int res;
 
     /* Check that the option specified a string as well as the input */
@@ -308,11 +333,21 @@ do_curl_setopt_string_impl(CurlObject *self, int option, PyObject *obj)
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
     case CURLOPT_PROXY_CAPATH:
     case CURLOPT_PROXY_CAINFO:
+    case CURLOPT_PROXY_CRLFILE:
     case CURLOPT_PRE_PROXY:
     case CURLOPT_PROXY_SSLCERT:
     case CURLOPT_PROXY_SSLCERTTYPE:
     case CURLOPT_PROXY_SSLKEY:
     case CURLOPT_PROXY_SSLKEYTYPE:
+    case CURLOPT_PROXY_KEYPASSWD:
+    case CURLOPT_PROXY_PINNEDPUBLICKEY:
+    case CURLOPT_PROXY_SSL_CIPHER_LIST:
+    case CURLOPT_PROXY_TLSAUTH_TYPE:
+    case CURLOPT_PROXY_TLSAUTH_USERNAME:
+    case CURLOPT_PROXY_TLSAUTH_PASSWORD:
+#endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 55, 0)
+    case CURLOPT_REQUEST_TARGET:
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 61, 0)
     case CURLOPT_TLS13_CIPHERS:
@@ -320,6 +355,12 @@ do_curl_setopt_string_impl(CurlObject *self, int option, PyObject *obj)
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 62, 0)
     case CURLOPT_DOH_URL:
+#endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    case CURLOPT_PROXY_ISSUERCERT:
+#endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 75, 0)
+    case CURLOPT_AWS_SIGV4:
 #endif
     case CURLOPT_KRBLEVEL:
         str = PyText_AsString_NoNUL(obj, &encoded_obj);
@@ -340,6 +381,33 @@ do_curl_setopt_string_impl(CurlObject *self, int option, PyObject *obj)
             CURLERROR_RETVAL();
         }
         break;
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    case CURLOPT_SSLCERT_BLOB:
+    case CURLOPT_SSLKEY_BLOB:
+    case CURLOPT_PROXY_SSLCERT_BLOB:
+    case CURLOPT_PROXY_SSLKEY_BLOB:
+    case CURLOPT_ISSUERCERT_BLOB:
+    case CURLOPT_PROXY_ISSUERCERT_BLOB:
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 77, 0)
+    case CURLOPT_CAINFO_BLOB:
+    case CURLOPT_PROXY_CAINFO_BLOB:
+#endif
+        if (PyText_AsStringAndSize(obj, &str, &len, &encoded_obj) != 0)
+            return NULL;
+
+        curlblob.data = str;
+        curlblob.len = len;
+        curlblob.flags = CURL_BLOB_COPY;
+
+        res = curl_easy_setopt(self->handle, (CURLoption)option, &curlblob);
+        if (res != CURLE_OK) {
+            PyText_EncodedDecref(encoded_obj);
+            CURLERROR_RETVAL();
+        }
+        PyText_EncodedDecref(encoded_obj);
+        Py_RETURN_NONE;
+        break;
+#endif
     default:
         PyErr_SetString(PyExc_TypeError, "strings are not supported for this option");
         return NULL;
@@ -1034,8 +1102,13 @@ do_curl_setopt(CurlObject *self, PyObject *args)
     /* early checks of option value */
     if (option <= 0)
         goto error;
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    if (option >= (int)CURLOPTTYPE_BLOB + OPTIONS_SIZE)
+        goto error;
+#else
     if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE)
         goto error;
+#endif
     if (option % 10000 >= OPTIONS_SIZE)
         goto error;
 
index 5326df376c69aec0935e11201d3d44f72dcf7ebc..a1570a3fee0958b4a39ba546030282ada37b1468 100644 (file)
@@ -4,7 +4,7 @@
 /* --------------- perform --------------- */
 
 PYCURL_INTERNAL PyObject *
-do_curl_perform(CurlObject *self)
+do_curl_perform(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     int res;
 
@@ -24,7 +24,7 @@ do_curl_perform(CurlObject *self)
 
 
 PYCURL_INTERNAL PyObject *
-do_curl_perform_rb(CurlObject *self)
+do_curl_perform_rb(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyObject *v, *io;
     
@@ -49,7 +49,7 @@ do_curl_perform_rb(CurlObject *self)
         return NULL;
     }
     
-    v = do_curl_perform(self);
+    v = do_curl_perform(self, NULL);
     if (v == NULL) {
         return NULL;
     }
@@ -61,11 +61,11 @@ do_curl_perform_rb(CurlObject *self)
 
 #if PY_MAJOR_VERSION >= 3
 PYCURL_INTERNAL PyObject *
-do_curl_perform_rs(CurlObject *self)
+do_curl_perform_rs(CurlObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyObject *v, *decoded;
     
-    v = do_curl_perform_rb(self);
+    v = do_curl_perform_rb(self, NULL);
     if (v == NULL) {
         return NULL;
     }
index d55d3ebe9ab15344106b53e1bc3214aa6186feff..e56a011d7a79f3d110b1f5026ab20c5e74f95d36 100644 (file)
@@ -94,7 +94,7 @@ do_global_init(PyObject *dummy, PyObject *args)
 
 
 PYCURL_INTERNAL PyObject *
-do_global_cleanup(PyObject *dummy)
+do_global_cleanup(PyObject *dummy, PyObject *Py_UNUSED(ignored))
 {
     UNUSED(dummy);
     curl_global_cleanup();
@@ -375,6 +375,7 @@ initpycurl(void)
                 case CURLSSLBACKEND_GNUTLS:
                 case CURLSSLBACKEND_NSS:
                 case CURLSSLBACKEND_WOLFSSL:
+                case CURLSSLBACKEND_SCHANNEL:
                 case CURLSSLBACKEND_MBEDTLS:
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 64, 1)
                 case CURLSSLBACKEND_SECURETRANSPORT:
@@ -413,6 +414,8 @@ initpycurl(void)
         runtime_ssl_lib = "mbedtls";
     } else if (!strncmp(vi->ssl_version, "Secure Transport", 16)) {
         runtime_ssl_lib = "secure-transport";
+    } else if (!strncmp(vi->ssl_version, "Schannel", 8)) {
+        runtime_ssl_lib = "schannel";
     } else {
         runtime_ssl_lib = "none/other";
     }
@@ -506,6 +509,7 @@ initpycurl(void)
     insstr_modinit(d, "version", g_pycurl_useragent);
     insint(d, "COMPILE_PY_VERSION_HEX", PY_VERSION_HEX);
     insint(d, "COMPILE_LIBCURL_VERSION_NUM", LIBCURL_VERSION_NUM);
+    insstr_modinit(d, "COMPILE_SSL_LIB", COMPILE_SSL_LIB);
 
     /* Types */
     insobj2_modinit(d, NULL, "Curl", (PyObject *) p_Curl_Type);
@@ -818,6 +822,18 @@ initpycurl(void)
 #endif
     insint_c(d, "HTTPPOST", CURLOPT_HTTPPOST);
     insint_c(d, "SSLCERT", CURLOPT_SSLCERT);
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    insint_c(d, "SSLCERT_BLOB", CURLOPT_SSLCERT_BLOB);
+    insint_c(d, "SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB);
+    insint_c(d, "PROXY_SSLCERT_BLOB", CURLOPT_PROXY_SSLCERT_BLOB);
+    insint_c(d, "PROXY_SSLKEY_BLOB", CURLOPT_PROXY_SSLKEY_BLOB);
+    insint_c(d, "ISSUERCERT_BLOB", CURLOPT_ISSUERCERT_BLOB);
+    insint_c(d, "PROXY_ISSUERCERT_BLOB", CURLOPT_PROXY_ISSUERCERT_BLOB);
+#endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 77, 0)
+    insint_c(d, "CAINFO_BLOB", CURLOPT_CAINFO_BLOB);
+    insint_c(d, "PROXY_CAINFO_BLOB", CURLOPT_PROXY_CAINFO_BLOB);
+#endif
     insint_c(d, "SSLCERTPASSWD", CURLOPT_SSLCERTPASSWD);
     insint_c(d, "CRLF", CURLOPT_CRLF);
     insint_c(d, "QUOTE", CURLOPT_QUOTE);
@@ -967,13 +983,28 @@ initpycurl(void)
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
     insint_c(d, "PROXY_CAPATH", CURLOPT_PROXY_CAPATH);
     insint_c(d, "PROXY_CAINFO", CURLOPT_PROXY_CAINFO);
+    insint_c(d, "PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE);
     insint_c(d, "PRE_PROXY", CURLOPT_PRE_PROXY);
     insint_c(d, "PROXY_SSLCERT", CURLOPT_PROXY_SSLCERT);
     insint_c(d, "PROXY_SSLCERTTYPE", CURLOPT_PROXY_SSLCERTTYPE);
     insint_c(d, "PROXY_SSLKEY", CURLOPT_PROXY_SSLKEY);
     insint_c(d, "PROXY_SSLKEYTYPE", CURLOPT_PROXY_SSLKEYTYPE);
+    insint_c(d, "PROXY_KEYPASSWD", CURLOPT_PROXY_KEYPASSWD);
     insint_c(d, "PROXY_SSL_VERIFYPEER", CURLOPT_PROXY_SSL_VERIFYPEER);
     insint_c(d, "PROXY_SSL_VERIFYHOST", CURLOPT_PROXY_SSL_VERIFYHOST);
+    insint_c(d, "PROXY_PINNEDPUBLICKEY", CURLOPT_PROXY_PINNEDPUBLICKEY);
+    insint_c(d, "PROXY_SSLVERSION", CURLOPT_PROXY_SSLVERSION);
+    insint_c(d, "PROXY_SSL_CIPHER_LIST", CURLOPT_PROXY_SSL_CIPHER_LIST);
+    insint_c(d, "PROXY_SSL_OPTIONS", CURLOPT_PROXY_SSL_OPTIONS);
+    insint_c(d, "PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE);
+    insint_c(d, "PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME);
+    insint_c(d, "PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD);
+#endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 71, 0)
+    insint_c(d, "PROXY_ISSUERCERT", CURLOPT_PROXY_ISSUERCERT);
+#endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 55, 0)
+    insint_c(d, "REQUEST_TARGET", CURLOPT_REQUEST_TARGET);
 #endif
     insint_c(d, "COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS);
     insint_c(d, "SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5);
@@ -1087,6 +1118,9 @@ initpycurl(void)
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 64, 0)
     insint_c(d, "HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED);
 #endif
+#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 75, 0)
+    insint_c(d, "AWS_SIGV4", CURLOPT_AWS_SIGV4);
+#endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 80, 0)
     insint_c(d, "MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN);
 #endif
@@ -1409,28 +1443,37 @@ initpycurl(void)
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
     insint(d, "CURL_VERSION_HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY);
+    insint(d, "VERSION_HTTPS_PROXY", CURL_VERSION_HTTPS_PROXY);
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 56, 0)
     insint(d, "CURL_VERSION_MULTI_SSL", CURL_VERSION_MULTI_SSL);
+    insint(d, "VERSION_MULTI_SSL", CURL_VERSION_MULTI_SSL);
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 57, 0)
     insint(d, "CURL_VERSION_BROTLI", CURL_VERSION_BROTLI);
+    insint(d, "VERSION_BROTLI", CURL_VERSION_BROTLI);
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 64, 1)
     insint(d, "CURL_VERSION_ALTSVC", CURL_VERSION_ALTSVC);
+    insint(d, "VERSION_ALTSVC", CURL_VERSION_ALTSVC);
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0)
     insint(d, "CURL_VERSION_HTTP3", CURL_VERSION_HTTP3);
+    insint(d, "VERSION_HTTP3", CURL_VERSION_HTTP3);
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 72, 0)
     insint(d, "CURL_VERSION_UNICODE", CURL_VERSION_UNICODE);
     insint(d, "CURL_VERSION_ZSTD", CURL_VERSION_ZSTD);
+    insint(d, "VERSION_UNICODE", CURL_VERSION_UNICODE);
+    insint(d, "VERSION_ZSTD", CURL_VERSION_ZSTD);
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 74, 0)
     insint(d, "CURL_VERSION_HSTS", CURL_VERSION_HSTS);
+    insint(d, "VERSION_HSTS", CURL_VERSION_HSTS);
 #endif
 #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 76, 0)
     insint(d, "CURL_VERSION_GSASL", CURL_VERSION_GSASL);
+    insint(d, "VERSION_GSASL", CURL_VERSION_GSASL);
 #endif
 
     /**
index 3dbc3fc67dd458ec26fc138e2792840db7d34421..12c11253fa221f4b0e29cd78fc6d750d34815efc 100644 (file)
@@ -132,7 +132,7 @@ do_multi_dealloc(CurlMultiObject *self)
 
 
 static PyObject *
-do_multi_close(CurlMultiObject *self)
+do_multi_close(CurlMultiObject *self, PyObject *Py_UNUSED(ignored))
 {
     if (check_multi_state(self, 2, "close") != 0) {
         return NULL;
@@ -492,7 +492,7 @@ error:
 /* --------------- timeout --------------- */
 
 static PyObject *
-do_multi_timeout(CurlMultiObject *self)
+do_multi_timeout(CurlMultiObject *self, PyObject *Py_UNUSED(ignored))
 {
     CURLMcode res;
     long timeout;
@@ -565,7 +565,7 @@ do_multi_socket_action(CurlMultiObject *self, PyObject *args)
 /* --------------- socket_all --------------- */
 
 static PyObject *
-do_multi_socket_all(CurlMultiObject *self)
+do_multi_socket_all(CurlMultiObject *self, PyObject *Py_UNUSED(ignored))
 {
     CURLMcode res;
     int running = -1;
@@ -591,7 +591,7 @@ do_multi_socket_all(CurlMultiObject *self)
 /* --------------- perform --------------- */
 
 static PyObject *
-do_multi_perform(CurlMultiObject *self)
+do_multi_perform(CurlMultiObject *self, PyObject *Py_UNUSED(ignored))
 {
     CURLMcode res;
     int running = -1;
@@ -734,7 +734,7 @@ done:
 /* --------------- fdset ---------------------- */
 
 static PyObject *
-do_multi_fdset(CurlMultiObject *self)
+do_multi_fdset(CurlMultiObject *self, PyObject *Py_UNUSED(ignored))
 {
     CURLMcode res;
     int max_fd = -1, fd;
@@ -821,13 +821,12 @@ do_multi_info_read(CurlMultiObject *self, PyObject *args)
     if ((ok_list = PyList_New((Py_ssize_t)0)) == NULL) goto error;
     if ((err_list = PyList_New((Py_ssize_t)0)) == NULL) goto error;
 
-    /* Loop through all messages */
-    while ((msg = curl_multi_info_read(self->multi_handle, &in_queue)) != NULL) {
+    /* Loop through up to 'num_results' messages */
+    while (num_results-- > 0) {
         CURLcode res;
         CurlObject *co = NULL;
 
-        /* Check for termination as specified by the user */
-        if (num_results-- <= 0) {
+        if ((msg = curl_multi_info_read(self->multi_handle, &in_queue)) == NULL) {
             break;
         }
 
@@ -935,7 +934,7 @@ do_multi_select(CurlMultiObject *self, PyObject *args)
 }
 
 
-static PyObject *do_curlmulti_getstate(CurlMultiObject *self)
+static PyObject *do_curlmulti_getstate(CurlMultiObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyErr_SetString(PyExc_TypeError, "CurlMulti objects do not support serialization");
     return NULL;
index 9a97f0bce689d15a5442b3a79faa753a55bea701..90eb38cfb3cf234940b75b5b331fc8158baf279c 100644 (file)
@@ -223,6 +223,9 @@ pycurl_inet_ntop (int family, void *addr, char *string, size_t string_size);
 # elif defined(HAVE_CURL_SECTRANSP)
 #   define COMPILE_SSL_LIB "secure-transport"
 #   define COMPILE_SUPPORTED_SSL_BACKEND_FOUND 1
+# elif defined(HAVE_CURL_SCHANNEL)
+#   define COMPILE_SSL_LIB "schannel"
+#   define COMPILE_SUPPORTED_SSL_BACKEND_FOUND 1
 # else
 #  ifdef _MSC_VER
     /* sigh */
@@ -240,7 +243,7 @@ pycurl_inet_ntop (int family, void *addr, char *string, size_t string_size);
     * no reason to require users match those */
 #  define COMPILE_SSL_LIB "none/other"
 #  define COMPILE_SUPPORTED_SSL_BACKEND_FOUND 0
-# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_WOLFSSL || HAVE_CURL_GNUTLS || HAVE_CURL_NSS || HAVE_CURL_MBEDTLS || HAVE_CURL_SECTRANSP */
+# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_WOLFSSL || HAVE_CURL_GNUTLS || HAVE_CURL_NSS || HAVE_CURL_MBEDTLS || HAVE_CURL_SECTRANSP || HAVE_CURL_SCHANNEL */
 #else
 # define COMPILE_SSL_LIB "none/other"
 # define COMPILE_SUPPORTED_SSL_BACKEND_FOUND 0
@@ -555,7 +558,7 @@ assert_curl_state(const CurlObject *self);
 PYCURL_INTERNAL PyObject *
 do_global_init(PyObject *dummy, PyObject *args);
 PYCURL_INTERNAL PyObject *
-do_global_cleanup(PyObject *dummy);
+do_global_cleanup(PyObject *dummy, PyObject *Py_UNUSED(ignored));
 PYCURL_INTERNAL PyObject *
 do_version_info(PyObject *dummy, PyObject *args);
 
@@ -570,12 +573,12 @@ PYCURL_INTERNAL PyObject *
 do_curl_set_ca_certs(CurlObject *self, PyObject *args);
 #endif
 PYCURL_INTERNAL PyObject *
-do_curl_perform(CurlObject *self);
+do_curl_perform(CurlObject *self, PyObject *Py_UNUSED(ignored));
 PYCURL_INTERNAL PyObject *
-do_curl_perform_rb(CurlObject *self);
+do_curl_perform_rb(CurlObject *self, PyObject *Py_UNUSED(ignored));
 #if PY_MAJOR_VERSION >= 3
 PYCURL_INTERNAL PyObject *
-do_curl_perform_rs(CurlObject *self);
+do_curl_perform_rs(CurlObject *self, PyObject *Py_UNUSED(ignored));
 #else
 # define do_curl_perform_rs do_curl_perform_rb
 #endif
@@ -604,10 +607,10 @@ do_curl_getinfo(CurlObject *self, PyObject *args);
 # define do_curl_getinfo do_curl_getinfo_raw
 #endif
 PYCURL_INTERNAL PyObject *
-do_curl_errstr(CurlObject *self);
+do_curl_errstr(CurlObject *self, PyObject *Py_UNUSED(ignored));
 #if PY_MAJOR_VERSION >= 3
 PYCURL_INTERNAL PyObject *
-do_curl_errstr_raw(CurlObject *self);
+do_curl_errstr_raw(CurlObject *self, PyObject *Py_UNUSED(ignored));
 #else
 # define do_curl_errstr_raw do_curl_errstr
 #endif
index 94b25b441fe5c1a10b6ab890a2fe8a0de2c5750f..bc16ebcd2379b411721565006bef958030103e1d 100644 (file)
@@ -138,7 +138,7 @@ do_share_dealloc(CurlShareObject *self)
 
 
 static PyObject *
-do_share_close(CurlShareObject *self)
+do_share_close(CurlShareObject *self, PyObject *Py_UNUSED(ignored))
 {
     if (check_share_state(self, 2, "close") != 0) {
         return NULL;
@@ -212,7 +212,7 @@ error:
 }
 
 
-static PyObject *do_curlshare_getstate(CurlShareObject *self)
+static PyObject *do_curlshare_getstate(CurlShareObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyErr_SetString(PyExc_TypeError, "CurlShare objects do not support serialization");
     return NULL;
index f00416f4a9b1e573e8282ea247dddfbf7aa4f4ed..390fd0704273a1f92cdc869d334621c58cb00fd5 100644 (file)
@@ -4,10 +4,17 @@ static PyObject *
 create_error_object(CurlObject *self, int code)
 {
     PyObject *s, *v;
-    
-    s = PyText_FromString_Ignore(self->error);
-    if (s == NULL) {
-        return NULL;
+
+    if (strlen(self->error)) {
+        s = PyText_FromString_Ignore(self->error);
+        if (s == NULL) {
+            return NULL;
+        }
+    } else {
+        s = PyText_FromString_Ignore(curl_easy_strerror(code));
+        if (s == NULL) {
+            return NULL;
+        }
     }
     v = Py_BuildValue("(iO)", code, s);
     if (v == NULL) {
diff --git a/tests/bin/realpath b/tests/bin/realpath
new file mode 100755 (executable)
index 0000000..a2fc902
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+import sys
+import os.path
+
+if len(sys.argv) < 2:
+    sys.stderr.write("Usage: realpath path\n")
+    exit(2)
+
+sys.stdout.write(os.path.realpath(sys.argv[1]) + "\n")
diff --git a/tests/c/winsockdup.c b/tests/c/winsockdup.c
new file mode 100644 (file)
index 0000000..4a8bd7d
--- /dev/null
@@ -0,0 +1,66 @@
+#include <winsock2.h>
+#include <assert.h>
+
+#define sassert assert
+
+void dump() {
+       int err;
+       LPTSTR buf;
+       err = WSAGetLastError();
+       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, &buf, 0, NULL);
+       printf("%d %s\n", err, buf);
+       LocalFree(buf);
+}
+
+int main() {
+       SOCKET s1, s2, s1d, s2d;
+       int val, rv, size;
+       WSADATA wsadata;
+       WSAPROTOCOL_INFO pi;
+
+       rv = WSAStartup(MAKEWORD(2, 2), &wsadata);
+       assert(!rv);
+
+       s1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       assert(s1 > 0);
+       val = 1;
+       rv = setsockopt(s1, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
+       dump();
+       sassert(!rv);
+
+       s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       assert(s2 > 0);
+       val = 0;
+       rv = setsockopt(s2, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
+       sassert(!rv);
+
+       size = sizeof(val);
+       rv = getsockopt(s1, SOL_SOCKET, SO_KEEPALIVE, &val, &size);
+       assert(!rv);
+       printf("%d\n", val);
+       sassert(val == 1);
+
+       rv = getsockopt(s2, SOL_SOCKET, SO_KEEPALIVE, &val, &size);
+       assert(!rv);
+       sassert(val == 0);
+
+       rv = WSADuplicateSocket(s1, GetCurrentProcessId(), &pi);
+       assert(!rv);
+       s1d = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, &pi, 0, WSA_FLAG_OVERLAPPED);
+       assert(s1d > 0);
+       
+       rv = getsockopt(s1d, SOL_SOCKET, SO_KEEPALIVE, &val, &size);
+       assert(!rv);
+       printf("%d\n", val);
+       sassert(val == 1);
+
+       rv = WSADuplicateSocket(s2, GetCurrentProcessId(), &pi);
+       assert(!rv);
+       s2d = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, &pi, 0, WSA_FLAG_OVERLAPPED);
+       assert(s2d > 0);
+       
+       rv = getsockopt(s2d, SOL_SOCKET, SO_KEEPALIVE, &val, &size);
+       assert(!rv);
+       printf("%d\n", val);
+       sassert(val == 0);
+}
index 4b1c9a690023828df15fd373d4e4a7c19f28a316..55bbade4a300a57b565f357ca5af8bf3c1da112e 100755 (executable)
@@ -1,4 +1,4 @@
-# 
+#!/bin/sh
 
 dir=$(dirname "$0")
 
diff --git a/tests/matrix/check-python.py b/tests/matrix/check-python.py
new file mode 100644 (file)
index 0000000..862efed
--- /dev/null
@@ -0,0 +1,4 @@
+import zlib
+import ssl
+
+dict(zlib=zlib, ssl=ssl)
index bbb13f4f2d9bb1d07a6c0776879622e50c889216..eb2e034b247c6ebfe108421515a6d85b142c8060 100644 (file)
@@ -52,7 +52,7 @@ class MultiCallbackTest(unittest.TestCase):
 
     # multi.socket_action must call both SOCKETFUNCTION and TIMERFUNCTION at
     # various points during the transfer (at least at the start and end)
-    @pytest.mark.xfail(sys.platform == 'darwin', reason='https://github.com/pycurl/pycurl/issues/729')
+    @pytest.mark.xfail(sys.platform in ['darwin', 'win32'], reason='https://github.com/pycurl/pycurl/issues/729')
     def test_multi_socket_action(self):
         self.multi.add_handle(self.easy)
         self.timer_result = None
@@ -77,7 +77,8 @@ class MultiCallbackTest(unittest.TestCase):
 
     # (mid-transfer) easy.pause(PAUSE_ALL) must call SOCKETFUNCTION to remove sockets
     # (mid-transfer) easy.pause(PAUSE_CONT) must call TIMERFUNCTION to resume
-    @pytest.mark.xfail(sys.platform == 'darwin', reason='https://github.com/pycurl/pycurl/issues/729')
+    @pytest.mark.xfail(reason='https://github.com/pycurl/pycurl/issues/729')
+    @pytest.mark.skipif(sys.platform == 'win32', reason='https://github.com/pycurl/pycurl/issues/819')
     def test_easy_pause_unpause(self):
         self.partial_transfer()
         self.socket_result = None
@@ -93,6 +94,7 @@ class MultiCallbackTest(unittest.TestCase):
 
     # (mid-transfer) easy.close() must call SOCKETFUNCTION to remove sockets
     @pytest.mark.xfail(sys.platform == 'darwin', reason='https://github.com/pycurl/pycurl/issues/729')
+    @pytest.mark.skipif(sys.platform == 'win32', reason='https://github.com/pycurl/pycurl/issues/819')
     def test_easy_close(self):
         self.partial_transfer()
         self.socket_result = None
index 5ce7fe8c7ccdd2b66759134fb758526ad5af92cd..8168f9d5ba2bc490b49299f54710979a80a68ebb 100644 (file)
@@ -4,8 +4,10 @@
 
 from . import localhost
 import pycurl
+import pytest
 import unittest
 import select
+import sys
 import flaky
 
 from . import appmanager
@@ -27,6 +29,7 @@ def teardown_module(mod):
 
 @flaky.flaky(max_runs=3)
 class MultiSocketSelectTest(unittest.TestCase):
+    @pytest.mark.skipif(sys.platform == 'win32', reason='https://github.com/pycurl/pycurl/issues/819')
     def test_multi_socket_select(self):
         sockets = set()
         timeout = 0
index 18cb5b68f889d71e8f7c8940bc29961a41d33287..182d415e47279a70e05aa4dffab9f2d0a839f2f5 100644 (file)
@@ -360,6 +360,50 @@ class MultiTest(unittest.TestCase):
         self.assertEqual('success', c2.body.getvalue().decode())
         self.assertEqual('success', c3.body.getvalue().decode())
 
+    def test_multi_info_read_some(self):
+        """
+        Check for missing messages from info_read when restricted to less than all messages
+
+        This is a regression check for an issue where the (n+1)'th queued message went
+        missing when (n < number of messages in the queue) and info_read(num_messages=n) was
+        called.
+        """
+        c1 = util.DefaultCurl()
+        c2 = util.DefaultCurl()
+        c3 = util.DefaultCurl()
+        c1.setopt(c1.URL, "http://%s:8380/short_wait" % localhost)
+        c2.setopt(c2.URL, "http://%s:8381/short_wait" % localhost)
+        c3.setopt(c3.URL, "http://%s:8382/short_wait" % localhost)
+        c1.body = util.BytesIO()
+        c2.body = util.BytesIO()
+        c3.body = util.BytesIO()
+        c1.setopt(c1.WRITEFUNCTION, c1.body.write)
+        c2.setopt(c2.WRITEFUNCTION, c2.body.write)
+        c3.setopt(c3.WRITEFUNCTION, c3.body.write)
+
+        m = pycurl.CurlMulti()
+        m.add_handle(c1)
+        m.add_handle(c2)
+        m.add_handle(c3)
+
+        # Complete all requests
+        num_handles = -1
+        ret = pycurl.E_CALL_MULTI_PERFORM
+        while num_handles:
+            if ret != pycurl.E_CALL_MULTI_PERFORM:
+                m.select(1.0)
+            ret, num_handles = m.perform()
+
+        # Three messages in the queue, read two
+        remaining, success, error = m.info_read(2)
+        assert remaining == 1
+        assert len(success) + len(error) == 2
+
+        # One message left in the queue
+        remaining, success, error = m.info_read()
+        assert remaining == 0
+        assert len(success) + len(error) == 1
+
     def test_multi_close(self):
         m = pycurl.CurlMulti()
         m.close()
index 479c4e71032e81702f2816647e0284e8773e9541..055dc58c7c59301fee68f7ac59bc27adb5c05b18 100644 (file)
@@ -269,6 +269,46 @@ class OptionConstantsTest(unittest.TestCase):
         curl.setopt(curl.CAPATH, '/bogus-capath')
         curl.close()
 
+    @util.min_libcurl(7, 77, 0)
+    @util.only_ssl_backends('openssl')
+    def test_cainfo_blob(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.CAINFO_BLOB, 'bogus-cainfo-blob-as-str')
+        curl.setopt(curl.CAINFO_BLOB, None)
+        curl.setopt(curl.CAINFO_BLOB, b'bogus-cainfo-blob-as-bytes')
+        curl.unsetopt(curl.CAINFO_BLOB)
+        curl.close()
+
+    @util.min_libcurl(7, 71, 0)
+    @util.only_ssl
+    def test_sslcert_blob(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.SSLCERT_BLOB, 'bogus-sslcert-blob-as-str')
+        curl.setopt(curl.SSLCERT_BLOB, None)
+        curl.setopt(curl.SSLCERT_BLOB, b'bogus-sslcert-blob-as-bytes')
+        curl.unsetopt(curl.SSLCERT_BLOB)
+        curl.close()
+
+    @util.min_libcurl(7, 71, 0)
+    @util.only_ssl
+    def test_sslkey_blob(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.SSLKEY_BLOB, 'bogus-sslkey-blob-as-str')
+        curl.setopt(curl.SSLKEY_BLOB, None)
+        curl.setopt(curl.SSLKEY_BLOB, b'bogus-sslkey-blob-as-bytes')
+        curl.unsetopt(curl.SSLKEY_BLOB)
+        curl.close()
+
+    @util.min_libcurl(7, 71, 0)
+    @util.only_ssl
+    def test_issuercert_blob(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.ISSUERCERT_BLOB, 'bogus-issuercert-blob-as-str')
+        curl.setopt(curl.ISSUERCERT_BLOB, None)
+        curl.setopt(curl.ISSUERCERT_BLOB, b'bogus-issuercert-blob-as-bytes')
+        curl.unsetopt(curl.ISSUERCERT_BLOB)
+        curl.close()
+
     # CURLOPT_PROXY_CAPATH was introduced in libcurl-7.52.0
     @util.min_libcurl(7, 52, 0)
     @util.only_ssl_backends('openssl', 'gnutls', 'nss')
@@ -277,6 +317,23 @@ class OptionConstantsTest(unittest.TestCase):
         curl.setopt(curl.PROXY_CAPATH, '/bogus-capath')
         curl.close()
 
+    @util.min_libcurl(7, 77, 0)
+    @util.only_ssl_backends('openssl')
+    def test_proxy_cainfo_blob(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_CAINFO_BLOB, 'bogus-cainfo-blob-as-str')
+        curl.setopt(curl.PROXY_CAINFO_BLOB, None)
+        curl.setopt(curl.PROXY_CAINFO_BLOB, b'bogus-cainfo-blob-as-bytes')
+        curl.unsetopt(curl.PROXY_CAINFO_BLOB)
+        curl.close()
+
+    @util.min_libcurl(7, 52, 0)
+    @util.only_ssl
+    def test_proxy_crlfile(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_CRLFILE, '/bogus-crlfile')
+        curl.close()
+
     @util.min_libcurl(7, 52, 0)
     @util.only_ssl
     def test_proxy_sslcert(self):
@@ -284,6 +341,16 @@ class OptionConstantsTest(unittest.TestCase):
         curl.setopt(curl.PROXY_SSLCERT, '/bogus-sslcert')
         curl.close()
 
+    @util.min_libcurl(7, 71, 0)
+    @util.only_ssl
+    def test_proxy_sslcert_blob(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_SSLCERT_BLOB, 'bogus-sslcert-blob-as-str')
+        curl.setopt(curl.PROXY_SSLCERT_BLOB, None)
+        curl.setopt(curl.PROXY_SSLCERT_BLOB, b'bogus-sslcert-blob-as-bytes')
+        curl.unsetopt(curl.PROXY_SSLCERT_BLOB)
+        curl.close()
+
     @util.min_libcurl(7, 52, 0)
     @util.only_ssl
     def test_proxy_sslcerttype(self):
@@ -298,6 +365,16 @@ class OptionConstantsTest(unittest.TestCase):
         curl.setopt(curl.PROXY_SSLKEY, '/bogus-sslkey')
         curl.close()
 
+    @util.min_libcurl(7, 71, 0)
+    @util.only_ssl
+    def test_proxy_sslkey_blob(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_SSLKEY_BLOB, 'bogus-sslkey-blob-as-str')
+        curl.setopt(curl.PROXY_SSLKEY_BLOB, None)
+        curl.setopt(curl.PROXY_SSLKEY_BLOB, b'bogus-sslkey-blob-as-bytes')
+        curl.unsetopt(curl.PROXY_SSLKEY_BLOB)
+        curl.close()
+
     @util.min_libcurl(7, 52, 0)
     @util.only_ssl
     def test_proxy_sslkeytype(self):
@@ -305,6 +382,23 @@ class OptionConstantsTest(unittest.TestCase):
         curl.setopt(curl.PROXY_SSLKEYTYPE, 'PEM')
         curl.close()
 
+    @util.min_libcurl(7, 71, 0)
+    @util.only_ssl
+    def test_proxy_issuercert_blob(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_ISSUERCERT_BLOB, 'bogus-issuercert-blob-as-str')
+        curl.setopt(curl.PROXY_ISSUERCERT_BLOB, None)
+        curl.setopt(curl.PROXY_ISSUERCERT_BLOB, b'bogus-issuercert-blob-as-bytes')
+        curl.unsetopt(curl.PROXY_ISSUERCERT_BLOB)
+        curl.close()
+
+    @util.min_libcurl(7, 52, 0)
+    @util.only_ssl
+    def test_proxy_keypasswd(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_KEYPASSWD, 'secret')
+        curl.close()
+
     @util.min_libcurl(7, 52, 0)
     @util.only_ssl
     def test_proxy_ssl_verifypeer(self):
@@ -319,6 +413,67 @@ class OptionConstantsTest(unittest.TestCase):
         curl.setopt(curl.PROXY_SSL_VERIFYHOST, 2)
         curl.close()
 
+    @util.min_libcurl(7, 52, 0)
+    @util.only_ssl
+    def test_proxy_pinnedpublickey(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_PINNEDPUBLICKEY, '/etc/publickey.der')
+        curl.close()
+
+    @util.min_libcurl(7, 52, 0)
+    @util.only_ssl
+    def test_proxy_sslversion_options(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_SSLVERSION, curl.SSLVERSION_DEFAULT)
+        curl.setopt(curl.PROXY_SSLVERSION, curl.SSLVERSION_TLSv1)
+        curl.setopt(curl.PROXY_SSLVERSION, curl.SSLVERSION_TLSv1_0)
+        curl.setopt(curl.PROXY_SSLVERSION, curl.SSLVERSION_TLSv1_1)
+        curl.setopt(curl.PROXY_SSLVERSION, curl.SSLVERSION_TLSv1_2)
+        curl.close()
+    # SSLVERSION_SSLv* return CURLE_BAD_FUNCTION_ARGUMENT with curl-7.77.0
+    @util.min_libcurl(7, 52, 0)
+    @util.removed_in_libcurl(7, 77, 0)
+    @util.only_ssl
+    def test_legacy_proxy_sslversion_options(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_SSLVERSION, curl.SSLVERSION_SSLv2)
+        curl.setopt(curl.PROXY_SSLVERSION, curl.SSLVERSION_SSLv3)
+        curl.close()
+
+    @util.min_libcurl(7, 52, 0)
+    @util.only_ssl
+    def test_proxy_ssl_cipher_list(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_SSL_CIPHER_LIST, 'RC4-SHA:SHA1+DES')
+        curl.close()
+
+    @util.min_libcurl(7, 52, 0)
+    @util.only_ssl
+    def test_proxy_ssl_options(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_SSL_OPTIONS, curl.SSLOPT_ALLOW_BEAST)
+        curl.setopt(curl.PROXY_SSL_OPTIONS, curl.SSLOPT_NO_REVOKE)
+        curl.close()
+
+    # Apparently TLSAUTH_TYPE=SRP is an unknown option on appveyor
+    @util.only_unix
+    @util.min_libcurl(7, 52, 0)
+    @util.only_ssl_backends('openssl', 'gnutls', 'schannel')
+    def test_proxy_tlsauth(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_TLSAUTH_TYPE, "SRP")
+        curl.setopt(curl.PROXY_TLSAUTH_USERNAME, "test")
+        curl.setopt(curl.PROXY_TLSAUTH_PASSWORD, "test")
+        curl.close()
+
+    @util.min_libcurl(7, 71, 0)
+    @util.only_ssl
+    def test_proxy_issuercert(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.PROXY_ISSUERCERT, '/bogus-issuercert')
+        curl.close()
+
     @util.only_ssl
     def test_crlfile(self):
         curl = pycurl.Curl()
@@ -331,7 +486,7 @@ class OptionConstantsTest(unittest.TestCase):
         curl.setopt(curl.RANDOM_FILE, '/bogus-random')
         curl.close()
 
-    @util.only_ssl_backends('openssl', 'gnutls', 'secure-transport')
+    @util.only_ssl_backends('openssl', 'gnutls', 'secure-transport', 'schannel')
     def test_egdsocket(self):
         curl = pycurl.Curl()
         curl.setopt(curl.EGDSOCKET, '/bogus-egdsocket')
@@ -373,6 +528,12 @@ class OptionConstantsTest(unittest.TestCase):
         curl.setopt(curl.SSL_OPTIONS, curl.SSLOPT_NO_REVOKE)
         curl.close()
 
+    @util.min_libcurl(7, 55, 0)
+    def test_request_target_option(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.REQUEST_TARGET, '*')
+        curl.close()
+
     @util.min_libcurl(7, 64, 0)
     def test_http09_allowed_option(self):
         curl = pycurl.Curl()
@@ -380,19 +541,25 @@ class OptionConstantsTest(unittest.TestCase):
         curl.close()
 
     @util.min_libcurl(7, 61, 0)
-    @util.only_ssl_backends('openssl')
+    @util.only_ssl_backends('openssl', 'schannel')
     def test_tls13_ciphers(self):
         curl = pycurl.Curl()
         curl.setopt(curl.TLS13_CIPHERS, 'TLS_CHACHA20_POLY1305_SHA256')
         curl.close()
 
     @util.min_libcurl(7, 61, 0)
-    @util.only_ssl_backends('openssl')
+    @util.only_ssl_backends('openssl', 'schannel')
     def test_proxy_tls13_ciphers(self):
         curl = pycurl.Curl()
         curl.setopt(curl.PROXY_TLS13_CIPHERS, 'TLS_CHACHA20_POLY1305_SHA256')
         curl.close()
 
+    @util.min_libcurl(7, 75, 0)
+    def test_aws_sigv4(self):
+        curl = pycurl.Curl()
+        curl.setopt(curl.AWS_SIGV4, 'provider1:provider2')
+        curl.close()
+
 class OptionConstantsSettingTest(unittest.TestCase):
     def setUp(self):
         self.curl = pycurl.Curl()
@@ -477,26 +644,27 @@ class OptionConstantsSettingTest(unittest.TestCase):
         self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_1)
 
     @util.min_libcurl(7, 33, 0)
-    @pytest.mark.http2
+    @util.only_http2
     def test_http_version_2_0(self):
         self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_2_0)
 
     @util.min_libcurl(7, 43, 0)
-    @pytest.mark.http2
+    @util.only_http2
     def test_http_version_2(self):
         self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_2)
 
     @util.min_libcurl(7, 47, 0)
-    @pytest.mark.http2
+    @util.only_http2
     def test_http_version_2tls(self):
         self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_2TLS)
 
     @util.min_libcurl(7, 49, 0)
-    @pytest.mark.http2
+    @util.only_http2
     def test_http_version_2prior_knowledge(self):
         self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
 
     @util.min_libcurl(7, 66, 0)
+    @util.only_http3
     def test_http_version_3(self):
         self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_3)
 
@@ -514,7 +682,7 @@ class OptionConstantsSettingTest(unittest.TestCase):
     # Apparently TLSAUTH_TYPE=SRP is an unknown option on appveyor
     @util.only_unix
     @util.min_libcurl(7, 21, 4)
-    @util.only_ssl_backends('openssl', 'gnutls')
+    @util.only_ssl_backends('openssl', 'gnutls', 'schannel')
     def test_tlsauth(self):
         self.curl.setopt(self.curl.TLSAUTH_TYPE, "SRP")
         self.curl.setopt(self.curl.TLSAUTH_USERNAME, "test")
index 50f94f7fcef09a35296f4dc8624ce5098e4870be..35d90f48ed64df3f8b63e278e2df896620a2339a 100644 (file)
@@ -161,8 +161,9 @@ not been able to determine which SSL backend it is using.' in captured_stderr.ge
     @util.only_unix
     @using_curl_config('curl-config-ssl-feature-only')
     def test_libcurl_ssl_openssl(self):
+        sopath = os.path.join(os.path.dirname(__file__), 'fake-curl', 'libcurl', 'with_openssl.so')
         config = pycurl_setup.ExtensionConfiguration(['',
-            '--libcurl-dll=tests/fake-curl/libcurl/with_openssl.so'])
+            '--libcurl-dll=' + sopath])
         # openssl should be detected
         assert 'HAVE_CURL_SSL' in config.define_symbols
         assert 'HAVE_CURL_OPENSSL' in config.define_symbols
@@ -174,8 +175,9 @@ not been able to determine which SSL backend it is using.' in captured_stderr.ge
     @util.only_unix
     @using_curl_config('curl-config-ssl-feature-only')
     def test_libcurl_ssl_gnutls(self):
+        sopath = os.path.join(os.path.dirname(__file__), 'fake-curl', 'libcurl', 'with_gnutls.so')
         config = pycurl_setup.ExtensionConfiguration(['',
-            '--libcurl-dll=tests/fake-curl/libcurl/with_gnutls.so'])
+            '--libcurl-dll=' + sopath])
         # gnutls should be detected
         assert 'HAVE_CURL_SSL' in config.define_symbols
         assert 'HAVE_CURL_GNUTLS' in config.define_symbols
@@ -187,8 +189,9 @@ not been able to determine which SSL backend it is using.' in captured_stderr.ge
     @util.only_unix
     @using_curl_config('curl-config-ssl-feature-only')
     def test_libcurl_ssl_nss(self):
+        sopath = os.path.join(os.path.dirname(__file__), 'fake-curl', 'libcurl', 'with_nss.so')
         config = pycurl_setup.ExtensionConfiguration(['',
-            '--libcurl-dll=tests/fake-curl/libcurl/with_nss.so'])
+            '--libcurl-dll=' + sopath])
         # nss should be detected
         assert 'HAVE_CURL_SSL' in config.define_symbols
         assert 'HAVE_CURL_NSS' in config.define_symbols
@@ -200,8 +203,9 @@ not been able to determine which SSL backend it is using.' in captured_stderr.ge
     @util.only_unix
     @using_curl_config('curl-config-empty')
     def test_libcurl_ssl_unrecognized(self):
+        sopath = os.path.join(os.path.dirname(__file__), 'fake-curl', 'libcurl', 'with_unknown.so')
         config = pycurl_setup.ExtensionConfiguration(['',
-            '--libcurl-dll=tests/fake-curl/libcurl/with_unknown_ssl.so'])
+            '--libcurl-dll=' + sopath])
         assert 'HAVE_CURL_SSL' not in config.define_symbols
         assert 'HAVE_CURL_OPENSSL' not in config.define_symbols
         assert 'HAVE_CURL_GNUTLS' not in config.define_symbols
@@ -258,8 +262,9 @@ not been able to determine which SSL backend it is using.' in captured_stderr.ge
     @util.only_unix
     @using_curl_config('curl-config-empty')
     def test_no_ssl_feature_with_libcurl_dll(self):
+        sopath = os.path.join(os.path.dirname(__file__), 'fake-curl', 'libcurl', 'with_openssl.so')
         config = pycurl_setup.ExtensionConfiguration(['',
-            '--libcurl-dll=tests/fake-curl/libcurl/with_openssl.so'])
+            '--libcurl-dll=' + sopath])
         # openssl should not be detected
         assert 'HAVE_CURL_SSL' not in config.define_symbols
         assert 'HAVE_CURL_OPENSSL' not in config.define_symbols
diff --git a/tests/travis/run.sh b/tests/travis/run.sh
new file mode 100755 (executable)
index 0000000..c71630f
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+set -e
+set -x
+
+export PATH=$HOME/opt/bin:$PATH
+
+if test -n "$USECURL"; then
+  # FTP support no longer ships with libcurl by default.
+  # Thus, only run ftp tests against libcurls that we built.
+  export PYCURL_VSFTPD_PATH=$HOME/opt/bin/vsftpd
+  
+  curldirname=curl-"$USECURL"
+  export PYCURL_CURL_CONFIG="$HOME"/opt/$curldirname/bin/curl-config
+  $PYCURL_CURL_CONFIG --features
+  export LD_LIBRARY_PATH="$HOME"/opt/$curldirname/lib
+fi
+
+setup_args=
+if test -n "$USESSL"; then
+  if test "$USESSL" = libressl; then
+    export PYCURL_SSL_LIBRARY=openssl
+    export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/opt/libressl-$USELIBRESSL/lib"
+    setup_args="$setup_args --openssl-dir=$HOME/opt/libressl-$USELIBRESSL"
+  elif test "$USESSL" != none; then
+    export PYCURL_SSL_LIBRARY="$USESSL"
+    if test -n "$USEOPENSSL"; then
+      export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/opt/openssl-$USEOPENSSL/lib"
+      setup_args="$setup_args --openssl-dir=$HOME/opt/openssl-$USEOPENSSL"
+    fi
+    if test -n "$USELIBRESSL"; then
+      export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/opt/libressl-$USELIBRESSL/lib"
+    fi
+  fi
+elif test -z "$USECURL"; then
+  # default for ubuntu 12 is openssl
+  # default for ubuntu 14 which is what travis currently uses is gnutls
+  export PYCURL_SSL_LIBRARY=gnutls
+fi
+
+if test -n "$AVOIDSTDIO"; then
+  export PYCURL_SETUP_OPTIONS=--avoid-stdio
+fi
+
+make gen
+python setup.py build $setup_args
+
+make -C tests/fake-curl/libcurl
+
+ldd build/lib*/pycurl*.so
+
+./tests/run.sh
+./tests/ext/test-suite.sh
+
+if test -n "$TESTDOCSEXAMPLES"; then
+  which pyflakes
+  pyflakes python examples tests setup.py
+  ./tests/run-quickstart.sh
+
+  # sphinx requires python 2.7+ or 3.3+
+  make docs
+fi
diff --git a/tests/travis/setup.sh b/tests/travis/setup.sh
new file mode 100755 (executable)
index 0000000..874b434
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+set -e
+set -x
+
+wget_once() {
+  url="$1"
+  if ! test -f `basename "$url"`; then
+    wget -O `basename "$url"`.part "$url"
+    rv=$?
+    if test $rv = 0; then
+      mv `basename "$url"`.part `basename "$url"`
+    else
+      rm -f `basename "$url"`.part
+      return $rv
+    fi
+  fi
+}
+
+file_host=https://github.com/pycurl/deps/raw/master
+distro=bionic
+ldlp=$LD_LIBRARY_PATH
+
+ldlp_exec() {
+  env LD_LIBRARY_PATH=$ldlp "$@"
+}
+
+(cd &&
+  mkdir -p opt &&
+  cd opt &&
+  wget $file_host/travis-$distro/bin-$distro-64.tar.xz &&
+  tar xfJ bin-$distro-64.tar.xz)
+
+export PATH=~/opt/bin:$PATH
+
+pip install -r requirements-dev.txt
+
+if test -n "$USECURL"; then
+  if test -n "$USEOPENSSL"; then
+    (cd && mkdir -p opt && cd opt &&
+      wget $file_host/travis-$distro/openssl-"$USEOPENSSL"-$distro-64.tar.xz &&
+      tar xfJ openssl-"$USEOPENSSL"-$distro-64.tar.xz)
+    ldlp=$ldlp:$HOME/opt/openssl-$USEOPENSSL/lib
+  fi
+  if test -n "$USELIBRESSL"; then
+    (cd && mkdir -p opt && cd opt &&
+      wget $file_host/travis-$distro/libressl-"$USELIBRESSL"-$distro-64.tar.xz &&
+      tar xfJ libressl-"$USELIBRESSL"-$distro-64.tar.xz)
+    ldlp=$ldlp:$HOME/opt/libressl-$USELIBRESSL/lib
+  fi
+
+  curldirname=curl-"$USECURL"
+  ldlp=$ldlp:$HOME/opt/$curldirname/lib
+  name=$curldirname-$distro-64.tar.xz
+  (cd &&
+    mkdir -p opt &&
+    cd opt &&
+    wget $file_host/travis-$distro/"$name" &&
+    tar xfJ "$name")
+  ldlp_exec "$HOME"/opt/$curldirname/bin/curl -V
+else
+  curl -V
+fi
index 43c3682f84293f7f9fb99cb7d2e4e60f6044109c..12ee8cea2acd006077370b8b7a09b69bdd7162f5 100644 (file)
@@ -66,7 +66,8 @@ def version_less_than_spec(version_tuple, spec_tuple):
 def pycurl_version_less_than(*spec):
     import pycurl
 
-    version = [int(part) for part in pycurl.version_info()[1].split('-')[0].split('.')]
+    c = pycurl.COMPILE_LIBCURL_VERSION_NUM
+    version = [c >> 16 & 0xFF, c >> 8 & 0xFF, c & 0xFF]
     return version_less_than_spec(version, spec)
 
 def only_python2(fn):
@@ -168,19 +169,8 @@ def only_ssl_backends(*backends):
             if 'https' not in pycurl.version_info()[8]:
                 raise unittest.SkipTest('libcurl does not support ssl')
 
-            # XXX move to pycurl library
-            if 'OpenSSL/' in pycurl.version:
-                current_backend = 'openssl'
-            elif 'GnuTLS/' in pycurl.version:
-                current_backend = 'gnutls'
-            elif 'NSS/' in pycurl.version:
-                current_backend = 'nss'
-            elif 'SecureTransport' in pycurl.version:
-                current_backend = 'secure-transport'
-            else:
-                current_backend = 'none'
-            if current_backend not in backends:
-                raise unittest.SkipTest('SSL backend is %s' % current_backend)
+            if pycurl.COMPILE_SSL_LIB not in backends:
+                raise unittest.SkipTest('SSL backend is %s' % pycurl.COMPILE_SSL_LIB)
 
             return fn(*args, **kwargs)
 
@@ -209,6 +199,30 @@ def only_unix(fn):
 
     return decorated
 
+def only_http2(fn):
+    import pycurl
+
+    @functools.wraps(fn)
+    def decorated(*args, **kwargs):
+        if not pycurl.version_info()[4] & pycurl.VERSION_HTTP2:
+            raise unittest.SkipTest('libcurl does not support HTTP version 2')
+
+        return fn(*args, **kwargs)
+
+    return decorated
+
+def only_http3(fn):
+    import pycurl
+
+    @functools.wraps(fn)
+    def decorated(*args, **kwargs):
+        if not pycurl.version_info()[4] & pycurl.VERSION_HTTP3:
+            raise unittest.SkipTest('libcurl does not support HTTP version 3')
+
+        return fn(*args, **kwargs)
+
+    return decorated
+
 def guard_unknown_libcurl_option(fn):
     '''Converts curl error 48, CURLE_UNKNOWN_OPTION, into a SkipTest
     exception. This is meant to be used with tests exercising libcurl
index a2b900961ff1f02a3647de9e71f2bdeb5f7a19ac..108471801884e47807bb97af7cf57d1d307994bf 100644 (file)
@@ -79,3 +79,34 @@ class VersionConstantsTest(unittest.TestCase):
     def test_psl(self):
         assert hasattr(pycurl, 'VERSION_PSL')
 
+    @util.min_libcurl(7, 52, 0)
+    def test_https_proxy(self):
+        assert hasattr(pycurl, 'VERSION_HTTPS_PROXY')
+
+    @util.min_libcurl(7, 56, 0)
+    def test_multi_ssl(self):
+        assert hasattr(pycurl, 'VERSION_MULTI_SSL')
+
+    @util.min_libcurl(7, 57, 0)
+    def test_brotli(self):
+        assert hasattr(pycurl, 'VERSION_BROTLI')
+
+    @util.min_libcurl(7, 64, 1)
+    def test_altsvc(self):
+        assert hasattr(pycurl, 'VERSION_ALTSVC')
+
+    @util.min_libcurl(7, 66, 0)
+    def test_http3(self):
+        assert hasattr(pycurl, 'VERSION_HTTP3')
+
+    @util.min_libcurl(7, 72, 0)
+    def test_unicode(self):
+        assert hasattr(pycurl, 'VERSION_UNICODE')
+
+    @util.min_libcurl(7, 72, 0)
+    def test_zstd(self):
+        assert hasattr(pycurl, 'VERSION_ZSTD')
+
+    @util.min_libcurl(7, 74, 0)
+    def test_hsts(self):
+        assert hasattr(pycurl, 'VERSION_HSTS')
diff --git a/tests/win/opensocketcrash.py b/tests/win/opensocketcrash.py
new file mode 100644 (file)
index 0000000..10bd080
--- /dev/null
@@ -0,0 +1,31 @@
+from . import localhost
+import pycurl
+from io import BytesIO
+import socket
+
+def socket_open(family, socktype, protocol, address):
+    global socket_open_called
+    global socket_open_address
+    socket_open_called = True
+    socket_open_address = address
+
+    #print(family, socktype, protocol, address)
+    s = socket.socket(family, socktype, protocol)
+    s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
+    print(2)
+    return s
+
+curl = pycurl.Curl()
+curl.setopt(pycurl.OPENSOCKETFUNCTION, socket_open)
+curl.setopt(curl.URL, 'http://%s:8380/success' % localhost)
+sio = BytesIO()
+curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+print(1)
+curl.perform()
+print(1)
+
+assert socket_open_called
+assert ("127.0.0.1", 8380) == socket_open_address
+assert 'success' == sio.getvalue().decode()
+
+print(1)
index 0a1af62bb31d6276118cf837f9e6361b8f305ce1..57d2c9ca072e1d49478842809dc85fd665099824 100644 (file)
@@ -103,7 +103,7 @@ class Config:
     # whether to build binary wheels
     build_wheels = True
     # pycurl version to build, we should know this ourselves
-    pycurl_version = '7.45.2'
+    pycurl_version = '7.45.3'
 
     # Sometimes vc14 does not include windows sdk path in vcvars which breaks stuff.
     # another application for this is to supply normaliz.lib for vc9