From bae84e03224817472bdc3a9bb70995ffbdaef620 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Morin <38703886+JeanChristopheMorinPerso@users.noreply.github.com> Date: Thu, 27 Oct 2022 13:19:01 -0400 Subject: [PATCH] Add support for editable installs (#1460) * Add support for editable installs Signed-off-by: Jean-Christophe Morin --- .gitignore | 2 ++ MANIFEST.in | 2 ++ docs/tutorials/quickstart.md | 2 +- setup.py | 58 +++++++++++++++++++----------------- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index afb5e9de6..dd2e1c7e4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ docs/_build .tox cpp_cov_html/ lcov_html_report/ +*.so +*.pyd diff --git a/MANIFEST.in b/MANIFEST.in index 64d9daf54..567cb83e0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -23,6 +23,8 @@ exclude */.DS_Store exclude .clang-format exclude OTIO_VERSION.json global-exclude *.pyc +global-exclude *.so +global-exclude *.pyd prune maintainers prune tsc diff --git a/docs/tutorials/quickstart.md b/docs/tutorials/quickstart.md index 34a80fe3c..283513b59 100644 --- a/docs/tutorials/quickstart.md +++ b/docs/tutorials/quickstart.md @@ -62,7 +62,7 @@ with OpenTimelineIO due to spaces in the path. ## To build OTIO for Python development: -+ `python -m pip install .` ++ `python -m pip install -e .` ## To build OTIO for both C++ and Python development: diff --git a/setup.py b/setup.py index 6986f3ec1..8624b8687 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,6 @@ import sys import platform import subprocess -import unittest import tempfile import shutil @@ -46,23 +45,30 @@ def join_args(args): return ' '.join(map(shlex.quote, args)) -class CMakeExtension(Extension): - def __init__(self, name): - Extension.__init__(self, name, sources=[]) - - class OTIO_build_ext(setuptools.command.build_ext.build_ext): """ def initialize_options(self): super(setuptools.command.build_ext.build_ext, self).initialize_options() """ + built = False + def run(self): - # This works around the fact that we build _opentime and _otio - # extensions as a one-shot cmake invocation. Usually we'd build each - # separately using build_extension. self.announce('running OTIO build_ext', level=2) - self.build() + # Let the original build_ext class do its job. + # This is rather important because build_ext.run takes care of a + # couple of things, one of which is to copy the built files into + # the source tree (in src/py-opentimelineio/opentimelineio) + # when building in editable mode. + super().run() + + def build_extension(self, _ext: Extension): + # This works around the fact that we build _opentime and _otio + # extensions as a one-shot cmake invocation. Setuptools calls + # build_extension for each Extension registered in the setup function. + if not self.built: + self.build() + self.built = True def build(self): self.build_temp_dir = ( @@ -256,22 +262,17 @@ class OTIO_build_py(setuptools.command.build_py.build_py): """Stamps PROJECT_METADATA into __init__ files.""" def run(self): - setuptools.command.build_py.build_py.run(self) - - if not self.dry_run: + super().run() + + if not self.dry_run and not self.editable_mode: + # Only run when not in dry-mode (a dry run should not have any side effect) + # and in non-editable mode. We don't want to edit files when in editable + # mode because that could lead to modifications to the source files. + # Note that setuptools will set self.editable_mode to True + # when "pip install -e ." is run. _append_version_info_to_init_scripts(self.build_lib) -def test_otio(): - """Discovers and runs tests""" - try: - # Clear the environment of a preset media linker - del os.environ['OTIO_DEFAULT_MEDIA_LINKER'] - except KeyError: - pass - return unittest.TestLoader().discover('tests') - - # copied from first paragraph of README.md LONG_DESCRIPTION = """OpenTimelineIO is an interchange format and API for editorial cut information. OTIO is not a container format for media, rather it @@ -339,8 +340,13 @@ def test_otio(): ), ext_modules=[ - CMakeExtension('_opentimelineio'), - CMakeExtension('_opentime'), + # The full and correct module name is required here because + # setuptools needs to resolve the name to find the built file + # and copy it into the source tree. (Because yes, editable install + # means that the install should point to the source tree, which + # means the .sos need to also be there alongside the python files). + Extension('opentimelineio._otio', sources=[]), + Extension('opentimelineio._opentime', sources=[]), ], package_dir={ @@ -382,8 +388,6 @@ def test_otio(): ] }, - test_suite='setup.test_otio', - # because we need to open() the adapters manifest, we aren't zip-safe zip_safe=False,