Downloading AppVeyor artifacts with a little bit of Python

I have recently released new versions of 3 of my Python modules (pyuv, pycares and python-fibers), which happen to be Python C extensions.

While preparing these releases, I decided to give AppVeyor a try, since it can be used for both integration testing on Windows and Python Wheels generation. I managed to do so following these instructions and checking this project example, and I was (almost) all set.

The missing part was to download all those built artifacts (the Python wheels) stored in AppVeyor and upload them to PyPI when I decided to make a release. Uploading the wheels can be easily done using twine, and for downloading the last built artifacts for a given project I created the following simple Python script using requests:

#!/usr/bin/env python
# coding=utf-8
# Author: Saúl Ibarra Corretgé <>
# License: MIT
import argparse
import multiprocessing
import requests
from concurrent.futures import ThreadPoolExecutor
def download_file(url):
local_filename = url.split('/')[1]
r = requests.get(url, stream=True)
with open(local_filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk: # filter out keep-alive new chunks
def get_file_urls(options):
session = requests.Session()
session.headers.update({'authorization': 'bearer %s' % options.api_token})
data = session.get(BASE_URL + '/projects/' + options.user + '/' + options.project)
data = data.json()
for job in (job['jobId'] for job in data['build']['jobs']):
job_url = BASE_URL + '/buildjobs/' + job + '/artifacts'
data = session.get(job_url)
data = data.json()
for item in data:
file_url = job_url + '/' + item['fileName']
yield file_url
def main(options):
with ThreadPoolExecutor(max_workers=multiprocessing.cpu_count()) as e:
for url in get_file_urls(options):
e.submit(download_file, url)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='AppVeyor artifact downloader')
parser.add_argument('–api-token', required=True)
parser.add_argument('–user', required=True)
parser.add_argument('–project', required=True)
args = parser.parse_args()

Using it is simple:

appveyor-download --api-token 1234 --user saghul --project pyuv

I hope you find it useful!



Leave a Reply