#!/usr/bin/env python # -*- coding: utf-8 -*- """ ISO Standards Setup. Standard setup.py file which is used to package and distribute a Python package with Distutils so that it can be published to a PyPI server. This file belongs in the root directory of a project repository. This is an example that uses some basic options which are available for the setup of a PyPI package. It also reads and gathers example dependencies for a package. See: https://pythonhosted.org/setuptools/setuptools.html for more details. """ import configparser import hashlib import os from ddex_ingester_common import __version__ from setuptools import Command from setuptools import find_packages from setuptools import setup class register(Command): """No-op: setup.py register was removed from setuptools.""" description = 'no-op register (removed in modern setuptools)' user_options = [('repository=', 'r', 'repository name / URL (ignored)')] def initialize_options(self): """No-op.""" self.repository = None def finalize_options(self): """No-op.""" def run(self): """No-op.""" class upload(Command): """Upload dist files to a PyPI server (replaces removed setuptools upload).""" # noqa description = 'upload dist files to a PyPI-compatible server' user_options = [('repository=', 'r', 'repository name from .pypirc')] def initialize_options(self): """Set default repository.""" self.repository = 'pypi' def finalize_options(self): """No-op.""" def run(self): """Read ~/.pypirc and POST each dist file to the repository.""" # Imported lazily so setup.py remains importable before requests is installed. # noqa import requests # noqa # Read repository URL and credentials from ~/.pypirc using the -r value rc_path = os.path.expanduser('~/.pypirc') cfg = configparser.RawConfigParser() if not cfg.read(rc_path): raise SystemExit(f'upload: {rc_path} not found') try: repo_url = cfg.get(self.repository, 'repository') username = cfg.get(self.repository, 'username') password = cfg.get(self.repository, 'password') except configparser.NoSectionError: raise SystemExit( f'upload: section [{self.repository}] not found in {rc_path}' ) # dist_files is populated by the preceding sdist/bdist_wheel commands # in the same invocation (e.g. `setup.py sdist bdist_wheel upload`). dist_files = getattr(self.distribution, 'dist_files', []) if not dist_files: raise SystemExit( 'upload: no dist files found; ' 'run sdist and/or bdist_wheel first' ) for _cmd, _pyver, filename in dist_files: # Compute MD5 in chunks to avoid loading artifacts into memory. digest = hashlib.md5() # noqa: S324 with open(filename, 'rb') as fh: for chunk in iter(lambda: fh.read(65536), b''): digest.update(chunk) # Stream the file directly to requests rather than buffering it. with open(filename, 'rb') as fh: resp = requests.post( repo_url, auth=(username, password), data={ ':action': 'file_upload', 'name': self.distribution.get_name(), 'version': self.distribution.get_version(), 'md5_digest': digest.hexdigest(), }, files={ 'content': ( os.path.basename(filename), fh, 'application/octet-stream', ) }, timeout=120, ) resp.raise_for_status() print(f'Uploaded {os.path.basename(filename)} to {repo_url}') def read_requirements(path): """Read requirements from file based on file path provided. Lines beginning with a dash are excluded, e.g. references to an internal PyPI server url (-i). Args: path (str): File path for requirements text file. Returns: Array of requirements libraries. """ with open(path, 'r') as fd: requirements = [ req.strip() for req in fd.readlines() if not req.startswith('-')] return requirements install_requires = list(read_requirements('requirements.txt')) setup( cmdclass={'register': register, 'upload': upload}, name='ddex_ingester_common', version=__version__, url='https://github.com/theorchard/lambda-ddex-ingester-common/', author='The Orchard', author_email='webdev@theorchard.com', description='Common package for DDEX Ingester.', packages=find_packages(), test_suite='tests', include_package_data=True, zip_safe=False, install_requires=install_requires, classifiers=[ 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11' ], )