#!/usr/bin/env python # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See LICENSE in the project root # for license information. import os import os.path import setuptools import subprocess import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import versioneer # noqa del sys.path[0] sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "src")) import debugpy import debugpy._vendored del sys.path[0] PYDEVD_ROOT = debugpy._vendored.project_root("pydevd") DEBUGBY_ROOT = os.path.dirname(os.path.abspath(debugpy.__file__)) def get_buildplatform(): if "-p" in sys.argv: return sys.argv[sys.argv.index("-p") + 1] return None # bdist_wheel determines whether the package is pure or not based on ext_modules. # However, all pydevd native modules are prebuilt and packaged as data, so they # should not be in the list. # # The proper way to handle this is by overriding has_ext_modules. However, due to # https://bugs.python.org/issue32957, in setuptools 57.0.0 and below, it is not # always called when it should be, with ext_modules tested directly instead. # # So, for non-pure builds, we provide a customized empty list for ext_modules that # tests as truthful - this causes the package to be treated as non-pure on all # relevant setuptools versions. class ExtModules(list): def __bool__(self): return True def override_build(cmds): def finalize_options(self): # Mark all packages as pure if requested to build a universal wheel. # Otherwise, bdist_wheel will silently ignore the request. bdist_wheel = self.distribution.get_command_obj("bdist_wheel", 0) if bdist_wheel is not None and bdist_wheel.universal: self.distribution.ext_modules = [] self.distribution.has_ext_modules = lambda: False original(self) try: build = cmds["build"] except KeyError: from distutils.command.build import build original = build.finalize_options build.finalize_options = finalize_options def override_build_py(cmds): # Add vendored packages as data files for this package, and filter platform-specific binaries # in data files for binary builds. def finalize_options(self): original(self) # Ensure that pydevd extensions are present for inclusion into data_files. self.announce( "Compiling pydevd Cython extension modules (set SKIP_CYTHON_BUILD=1 to omit)." ) try: subprocess.check_call([ sys.executable, os.path.join(PYDEVD_ROOT, "setup_pydevd_cython.py"), "build_ext", "--inplace", ]) except subprocess.SubprocessError: # pydevd Cython extensions are optional performance enhancements, and debugpy is # usable without them. Thus, we want to ignore build errors here by default, so # that a user can do `pip install debugpy` on a platform for which we don't supply # prebuild wheels even if they don't have the C build tools installed. On the other # hand, for our own official builds and for local development, we want to know if # the extensions fail to build. if int(os.getenv("REQUIRE_CYTHON_BUILD", "0")): raise self.warn( "Failed to compile pydevd Cython extension modules (set SKIP_CYTHON_BUILD=1 to omit); proceeding without them." ) # Register pydevd and other vendored packages as package data for debugpy. vendored_files = self.package_data["debugpy._vendored"] for project in debugpy._vendored.list_all(): for filename in debugpy._vendored.iter_packaging_files(project): vendored_files.append(filename) # Don't filter anything for universal wheels. if not self.distribution.ext_modules: return plat = self.get_finalized_command("build").plat_name def is_applicable(filename): def tail_is(*suffixes): return any((filename.endswith(ext) for ext in suffixes)) if tail_is(".dylib"): return plat.startswith("macosx") if tail_is(".exe", ".dll", ".pdb", ".pyd"): return plat in ("win32", "win-amd64") if tail_is("-i386-linux-gnu.so", "_linux_x86.so"): return plat == "linux-i686" if tail_is("-x86_64-linux-gnu.so", "_linux_amd64.so"): return plat == "linux-x86_64" return True vendored_files[:] = list(filter(is_applicable, vendored_files)) try: build_py = cmds["build_py"] except KeyError: from setuptools.command.build_py import build_py original = build_py.finalize_options build_py.finalize_options = finalize_options with open("DESCRIPTION.md", "r") as fh: long_description = fh.read() if __name__ == "__main__": extras = {} platforms = get_buildplatform() if platforms is not None: extras["platforms"] = platforms cmds = versioneer.get_cmdclass() override_build(cmds) override_build_py(cmds) setuptools.setup( name="debugpy", version=versioneer.get_version(), description="An implementation of the Debug Adapter Protocol for Python", # noqa long_description=long_description, long_description_content_type="text/markdown", license="MIT", author="Microsoft Corporation", author_email="ptvshelp@microsoft.com", url="https://aka.ms/debugpy", project_urls={ "Source": "https://github.com/microsoft/debugpy", }, python_requires=">=3.8", classifiers=[ "Development Status :: 5 - Production/Stable", "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", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Debuggers", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS", "Operating System :: POSIX", "License :: OSI Approved :: MIT License", ], package_dir={"": "src"}, packages=[ "debugpy", "debugpy.adapter", "debugpy.common", "debugpy.launcher", "debugpy.server", "debugpy._vendored", ], package_data={ "debugpy": ["ThirdPartyNotices.txt", "py.typed"], "debugpy._vendored": [ # pydevd extensions must be built before this list can be computed properly, # so it is populated in the overridden build_py.finalize_options(). ], }, ext_modules=ExtModules(), has_ext_modules=lambda: True, cmdclass=cmds, # allow the user to call "debugpy" instead of "python -m debugpy" entry_points={ "console_scripts": [ "debugpy = debugpy.server.cli:main", "debugpy-adapter = debugpy.adapter.__main__:main", ], }, **extras, )