Skip to content

Commit

Permalink
fix: make C extensions mandatory (#1333)
Browse files Browse the repository at this point in the history
If no C compiler is available, the installation do not fail and only a warning
is being print. This is wrong as the profiler won't work — at all.

Since we need a C compiler to build the profiler extensions that are part of
the wheel anyway,  let's not make wrapt or psutil optional.

Fixes #1328
  • Loading branch information
jd committed Apr 15, 2020
1 parent 0b8435f commit a6653c3
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 122 deletions.
6 changes: 2 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,11 @@ commands:
# Ensure we didn't cache from previous runs
- run: rm -rf build/ dist/
# Manually build any extensions to ensure they succeed
# DEV: `DDTRACE_BUILD_RAISE=TRUE` will ensure we don't swallow any build errors
- run: DDTRACE_BUILD_RAISE=TRUE python setup.py build_ext --force
- run: python setup.py build_ext --force
# Ensure source package will build
- run: python setup.py sdist
# Ensure wheel will build
# DEV: `DDTRACE_BUILD_RAISE=TRUE` will ensure we don't swallow any build errors
- run: DDTRACE_BUILD_RAISE=TRUE python setup.py bdist_wheel
- run: python setup.py bdist_wheel
# Ensure package long description is valid and will render
# https://github.com/pypa/twine/tree/6c4d5ecf2596c72b89b969ccc37b82c160645df8#twine-check
- run: twine check dist/*
Expand Down
5 changes: 5 additions & 0 deletions docs/installation_quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ If you want to use the profiler, you'll need to specify the ``profiling`` flavor

$ pip install ddtrace[profiling]


.. note::

The profiler does not work on Alpine Linux.

Quickstart
----------

Expand Down
189 changes: 71 additions & 118 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import copy
import os
import sys

from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand

Expand Down Expand Up @@ -90,101 +88,6 @@ def run_tests(self):
[visualization docs]: https://docs.datadoghq.com/tracing/visualization/
"""

# Base `setup()` kwargs without any C-extension registering
setup_kwargs = dict(
name="ddtrace",
description="Datadog tracing code",
url="https://github.com/DataDog/dd-trace-py",
author="Datadog, Inc.",
author_email="dev@datadoghq.com",
long_description=long_description,
long_description_content_type="text/markdown",
license="BSD",
packages=find_packages(exclude=["tests*"]),
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
# enum34 is an enum backport for earlier versions of python
# funcsigs backport required for vendored debtcollector
# encoding using msgpack
install_requires=["enum34; python_version<'3.4'", "funcsigs>=1.0.0; python_version=='2.7'", "msgpack>=0.5.0",],
extras_require={
# users can include opentracing by having:
# install_requires=['ddtrace[opentracing]', ...]
"opentracing": ["opentracing>=2.0.0"],
# TODO: remove me when everything is updated to `profiling`
"profile": ["protobuf>=3", "intervaltree",],
"profiling": ["protobuf>=3", "intervaltree",],
},
# plugin tox
tests_require=["tox", "flake8"],
cmdclass={"test": Tox, "build_ext": Cython.Distutils.build_ext},
entry_points={
"console_scripts": [
"ddtrace-run = ddtrace.commands.ddtrace_run:main",
"pyddprofile = ddtrace.profiling.__main__:main",
]
},
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
],
use_scm_version=True,
setup_requires=["setuptools_scm", "cython"],
ext_modules=cythonize(
[
Cython.Distutils.Extension(
"ddtrace.profiling.collector.stack",
sources=["ddtrace/profiling/collector/stack.pyx"],
language="c",
extra_compile_args=["-DPy_BUILD_CORE"],
),
Cython.Distutils.Extension(
"ddtrace.profiling.collector._traceback",
sources=["ddtrace/profiling/collector/_traceback.pyx"],
language="c",
),
Cython.Distutils.Extension(
"ddtrace.profiling._build", sources=["ddtrace/profiling/_build.pyx"], language="c",
),
],
compile_time_env={
"PY_MAJOR_VERSION": sys.version_info.major,
"PY_MINOR_VERSION": sys.version_info.minor,
"PY_MICRO_VERSION": sys.version_info.micro,
},
),
)


if sys.platform == "win32":
build_ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError, IOError, OSError)
else:
build_ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)


class BuildExtFailed(Exception):
pass


# Attempt to build a C-extension, catch exceptions so failed building skips the extension
# DEV: This is basically what `distutils`'s' `Extension(optional=True)` does
class optional_build_ext(Cython.Distutils.build_ext):
def run(self):
try:
Cython.Distutils.build_ext.run(self)
except DistutilsPlatformError as e:
extensions = [ext.name for ext in self.extensions]
print("WARNING: Failed to build extensions %r, skipping: %s" % (extensions, e))

def build_extension(self, ext):
try:
Cython.Distutils.build_ext.build_extension(self, ext)
except build_ext_errors as e:
print("WARNING: Failed to build extension %s, skipping: %s" % (ext.name, e))


def get_exts_for(name):
try:
Expand All @@ -197,24 +100,74 @@ def get_exts_for(name):
return []


# Try to build with C extensions first, fallback to only pure-Python if building fails
try:
all_exts = []
for extname in ("wrapt", "psutil"):
exts = get_exts_for(extname)
if exts:
all_exts.extend(exts)

kwargs = copy.deepcopy(setup_kwargs)
kwargs["ext_modules"] += all_exts
# DEV: Make sure `cmdclass` exists
kwargs.setdefault("cmdclass", dict())
kwargs["cmdclass"]["build_ext"] = optional_build_ext
setup(**kwargs)
except Exception as e:
# Set `DDTRACE_BUILD_TRACE=TRUE` in CI to raise any build errors
if os.environ.get("DDTRACE_BUILD_RAISE") == "TRUE":
raise

print("WARNING: Failed to install with ddtrace C-extensions, falling back to pure-Python only extensions: %s" % e)
setup(**setup_kwargs)
# Base `setup()` kwargs without any C-extension registering
setup(
**dict(
name="ddtrace",
description="Datadog tracing code",
url="https://github.com/DataDog/dd-trace-py",
author="Datadog, Inc.",
author_email="dev@datadoghq.com",
long_description=long_description,
long_description_content_type="text/markdown",
license="BSD",
packages=find_packages(exclude=["tests*"]),
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
# enum34 is an enum backport for earlier versions of python
# funcsigs backport required for vendored debtcollector
# encoding using msgpack
install_requires=["enum34; python_version<'3.4'", "funcsigs>=1.0.0; python_version=='2.7'", "msgpack>=0.5.0",],
extras_require={
# users can include opentracing by having:
# install_requires=['ddtrace[opentracing]', ...]
"opentracing": ["opentracing>=2.0.0"],
# TODO: remove me when everything is updated to `profiling`
"profile": ["protobuf>=3", "intervaltree",],
"profiling": ["protobuf>=3", "intervaltree",],
},
# plugin tox
tests_require=["tox", "flake8"],
cmdclass={"test": Tox, "build_ext": Cython.Distutils.build_ext},
entry_points={
"console_scripts": [
"ddtrace-run = ddtrace.commands.ddtrace_run:main",
"pyddprofile = ddtrace.profiling.__main__:main",
]
},
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
],
use_scm_version=True,
setup_requires=["setuptools_scm", "cython"],
ext_modules=cythonize(
[
Cython.Distutils.Extension(
"ddtrace.profiling.collector.stack",
sources=["ddtrace/profiling/collector/stack.pyx"],
language="c",
extra_compile_args=["-DPy_BUILD_CORE"],
),
Cython.Distutils.Extension(
"ddtrace.profiling.collector._traceback",
sources=["ddtrace/profiling/collector/_traceback.pyx"],
language="c",
),
Cython.Distutils.Extension(
"ddtrace.profiling._build", sources=["ddtrace/profiling/_build.pyx"], language="c",
),
],
compile_time_env={
"PY_MAJOR_VERSION": sys.version_info.major,
"PY_MINOR_VERSION": sys.version_info.minor,
"PY_MICRO_VERSION": sys.version_info.micro,
},
)
+ get_exts_for("wrapt")
+ get_exts_for("psutil"),
)
)

0 comments on commit a6653c3

Please sign in to comment.