#!/usr/bin/env python """setuptools module for contract_wrappers package.""" # pylint: disable=import-outside-toplevel # we import things outside of top-level because 3rd party libs may not yet be # installed when you invoke this script from glob import glob import subprocess # nosec from shutil import rmtree from os import environ, path, remove from pathlib import Path from sys import argv, exit # pylint: disable=redefined-builtin from distutils.command.clean import clean import distutils.command.build_py from setuptools import find_packages, setup from setuptools.command.test import test as TestCommand class PreInstallCommand(distutils.command.build_py.build_py): """Custom setuptools command class for pulling in generated code.""" description = "Pull in code generated by TypeScript" def run(self): """Use abi-gen to generate contract wrappers.""" subprocess.check_call("pip install black".split()) # nosec subprocess.check_call( # nosec ( "../../node_modules/.bin/abi-gen" " --language Python" " --abis ../../packages/contract-artifacts/artifacts/*" " --output src/zero_ex/contract_wrappers" ).split() ) class TestCommandExtension(TestCommand): """Run pytest tests.""" def run_tests(self): """Invoke pytest.""" import pytest exit(pytest.main(["--doctest-modules", "-rapP"])) # show short test summary at end ^ class LintCommand(distutils.command.build_py.build_py): """Custom setuptools command class for running linters.""" description = "Run linters" def run(self): """Run linter shell commands.""" lint_commands = [ # formatter: ( "black --line-length 79 --check --diff src test setup.py" ).split(), # style guide checker (formerly pep8): "pycodestyle src test setup.py".split(), # docstring style checker: "pydocstyle src test setup.py".split(), # static type checker: "mypy src test setup.py".split(), # security issue checker: "bandit -r src ./setup.py".split(), # general linter: "pylint src test setup.py".split(), # pylint takes relatively long to run, so it runs last, to enable # fast failures. ] # tell mypy where to find interface stubs for 3rd party libs environ["MYPYPATH"] = path.join( path.dirname(path.realpath(argv[0])), "stubs" ) # HACK(gene): until eth_utils fixes # https://github.com/ethereum/eth-utils/issues/140 , we need to simply # create an empty file `py.typed` in the eth_abi package directory. import eth_utils eth_utils_dir = path.dirname(path.realpath(eth_utils.__file__)) Path(path.join(eth_utils_dir, "py.typed")).touch() for lint_command in lint_commands: print( "Running lint command `", " ".join(lint_command).strip(), "`" ) subprocess.check_call(lint_command) # nosec class CleanCommandExtension(clean): """Custom command to do custom cleanup.""" def run(self): """Run the regular clean, followed by our custom commands.""" super().run() rmtree("build", ignore_errors=True) rmtree("dist", ignore_errors=True) rmtree(".mypy_cache", ignore_errors=True) rmtree(".tox", ignore_errors=True) rmtree(".pytest_cache", ignore_errors=True) rmtree( path.join("src", "0x_contract_wrappers.egg-info"), ignore_errors=True, ) # generated files: print("Removing src/zero_ex/contract_wrappers/*/__init__.py...") for contract in glob( path.join( ".", "src", "zero_ex", "contract_wrappers", "*", "__init__.py" ) ): try: print(f"Removing {contract}...", end="") remove(contract) print("done") except FileNotFoundError: print("file not found") class TestPublishCommand(distutils.command.build_py.build_py): """Custom command to publish to test.pypi.org.""" description = ( "Publish dist/* to test.pypi.org. Run sdist & bdist_wheel first." ) def run(self): """Run twine to upload to test.pypi.org.""" subprocess.check_call( # nosec ( "twine upload --repository-url https://test.pypi.org/legacy/" + " --verbose dist/*" ).split() ) class PublishCommand(distutils.command.build_py.build_py): """Custom command to publish to pypi.org.""" description = "Publish dist/* to pypi.org. Run sdist & bdist_wheel first." def run(self): """Run twine to upload to pypi.org.""" subprocess.check_call("twine upload dist/*".split()) # nosec class PublishDocsCommand(distutils.command.build_py.build_py): """Custom command to publish docs to S3.""" description = ( "Publish docs to " + "http://0x-contract-wrappers-py.s3-website-us-east-1.amazonaws.com/" ) def run(self): """Run npm package `discharge` to build & upload docs.""" subprocess.check_call("discharge deploy".split()) # nosec class GanacheCommand(distutils.command.build_py.build_py): """Custom command to publish to pypi.org.""" description = "Run ganache daemon to support tests." def run(self): """Run ganache.""" subprocess.call(("docker pull 0xorg/ganache-cli").split()) # nosec subprocess.call( # nosec ("docker run -d -p 8545:8545 0xorg/ganache-cli").split() ) with open("README.md", "r") as file_handle: README_MD = file_handle.read() setup( name="0x-contract-wrappers", version="2.0.0", description="Python wrappers for 0x smart contracts", long_description=README_MD, long_description_content_type="text/markdown", url=( "https://github.com/0xproject/0x-monorepo/tree/development" + "/python-packages/contract_wrappers" ), author="F. Eugene Aumson", author_email="feuGeneA@users.noreply.github.com", cmdclass={ "clean": CleanCommandExtension, "pre_install": PreInstallCommand, "lint": LintCommand, "test": TestCommandExtension, "test_publish": TestPublishCommand, "publish": PublishCommand, "publish_docs": PublishDocsCommand, "ganache": GanacheCommand, }, install_requires=[ "0x-contract-addresses", "0x-contract-artifacts", "0x-json-schemas", "0x-order-utils", "web3", "attrs", "eth_utils", "mypy_extensions", ], extras_require={ "dev": [ "bandit", "black", "coverage", "coveralls", "mypy<=0.770", # see https://github.com/python/mypy/issues/8953 "mypy_extensions", "pycodestyle", "pydocstyle", "pylint", "pytest", "sphinx", "tox", "twine", ] }, python_requires=">=3.6, <4", package_data={"zero_ex.contract_wrappers": ["py.typed"]}, package_dir={"": "src"}, license="Apache 2.0", keywords=( "ethereum cryptocurrency 0x decentralized blockchain dex exchange" ), namespace_packages=["zero_ex"], packages=find_packages("src"), classifiers=[ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "Intended Audience :: Financial and Insurance Industry", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Internet :: WWW/HTTP", "Topic :: Office/Business :: Financial", "Topic :: Other/Nonlisted Topic", "Topic :: Security :: Cryptography", "Topic :: Software Development :: Libraries", "Topic :: Utilities", ], zip_safe=False, # required per mypy command_options={ "build_sphinx": { "source_dir": ("setup.py", "src"), "build_dir": ("setup.py", "build/docs"), "warning_is_error": ("setup.py", "true"), } }, )