keras/pip_build.py
Haifeng Jin b14d0c21eb fix the --install arg doesn't work (#686)
Co-authored-by: Haifeng Jin <haifeng-jin@users.noreply.github.com>
2023-08-09 13:53:00 -07:00

201 lines
6.6 KiB
Python

"""Script to create (and optionally install) a `.whl` archive for Keras Core.
Usage:
1. Create a `.whl` file in `dist/`:
```
python3 pip_build.py
```
2. Also install the new package immediately after:
```
python3 pip_build.py --install
```
"""
import argparse
import glob
import os
import pathlib
import shutil
import namex
# Needed because importing torch after TF causes the runtime to crash
import torch # noqa: F401
package = "keras_core"
build_directory = "tmp_build_dir"
dist_directory = "dist"
to_copy = ["setup.py", "README.md"]
def ignore_files(_, filenames):
return [f for f in filenames if f.endswith("_test.py")]
def copy_source_to_build_directory(root_path):
# Copy sources (`keras_core/` directory and setup files) to build
# directory
os.chdir(root_path)
os.mkdir(build_directory)
shutil.copytree(
package, os.path.join(build_directory, package), ignore=ignore_files
)
for fname in to_copy:
shutil.copy(fname, os.path.join(f"{build_directory}", fname))
os.chdir(build_directory)
def run_namex_conversion():
# Restructure the codebase so that source files live in `keras_core/src`
namex.convert_codebase(package, code_directory="src")
# Generate API __init__.py files in `keras_core/`
namex.generate_api_files(package, code_directory="src", verbose=True)
def create_legacy_directory():
# Make keras_core/_tf_keras/ by copying keras_core/
tf_keras_dirpath = os.path.join(package, "_tf_keras")
os.makedirs(tf_keras_dirpath)
with open(os.path.join(package, "__init__.py")) as f:
init_file = f.read()
init_file = init_file.replace(
"from keras_core import _legacy",
"from keras_core import _tf_keras",
)
with open(os.path.join(package, "__init__.py"), "w") as f:
f.write(init_file)
with open(os.path.join(tf_keras_dirpath, "__init__.py"), "w") as f:
f.write(init_file)
for dirname in os.listdir(package):
dirpath = os.path.join(package, dirname)
if os.path.isdir(dirpath) and dirname not in (
"_legacy",
"_tf_keras",
"src",
):
shutil.copytree(
dirpath,
os.path.join(tf_keras_dirpath, dirname),
ignore=ignore_files,
)
# Copy keras_core/_legacy/ file contents to keras_core/_tf_keras/
legacy_submodules = [
path[:-3]
for path in os.listdir(os.path.join(package, "src", "legacy"))
if path.endswith(".py")
]
legacy_submodules += [
path
for path in os.listdir(os.path.join(package, "src", "legacy"))
if os.path.isdir(os.path.join(package, "src", "legacy", path))
]
for root, _, fnames in os.walk(os.path.join(package, "_legacy")):
for fname in fnames:
if fname.endswith(".py"):
legacy_fpath = os.path.join(root, fname)
tf_keras_root = root.replace("/_legacy", "/_tf_keras")
core_api_fpath = os.path.join(
root.replace("/_legacy", ""), fname
)
if not os.path.exists(tf_keras_root):
os.makedirs(tf_keras_root)
tf_keras_fpath = os.path.join(tf_keras_root, fname)
with open(legacy_fpath) as f:
legacy_contents = f.read()
legacy_contents = legacy_contents.replace(
"keras_core._legacy", "keras_core._tf_keras"
)
if os.path.exists(core_api_fpath):
with open(core_api_fpath) as f:
core_api_contents = f.read()
core_api_contents = core_api_contents.replace(
"from keras_core import _tf_keras\n", ""
)
for legacy_submodule in legacy_submodules:
core_api_contents = core_api_contents.replace(
f"from keras_core import {legacy_submodule}\n",
"",
)
core_api_contents = core_api_contents.replace(
f"keras_core.{legacy_submodule}",
f"keras_core._tf_keras.{legacy_submodule}",
)
legacy_contents = core_api_contents + "\n" + legacy_contents
with open(tf_keras_fpath, "w") as f:
f.write(legacy_contents)
# Delete keras_core/_legacy/
shutil.rmtree(os.path.join(package, "_legacy"))
def export_version_string(__version__):
# Make sure to export the __version__ string
with open(os.path.join(package, "__init__.py")) as f:
init_contents = f.read()
with open(os.path.join(package, "__init__.py"), "w") as f:
f.write(init_contents + "\n\n" + f'__version__ = "{__version__}"\n')
def build_and_save_output(root_path, __version__):
# Build the package
os.system("python3 -m build")
# Save the dist files generated by the build process
os.chdir(root_path)
if not os.path.exists(dist_directory):
os.mkdir(dist_directory)
for fpath in glob.glob(
os.path.join(build_directory, dist_directory, "*.*")
):
shutil.copy(fpath, dist_directory)
# Find the .whl file path
whl_path = None
for fname in os.listdir(dist_directory):
if __version__ in fname and fname.endswith(".whl"):
whl_path = os.path.abspath(os.path.join(dist_directory, fname))
print(f"Build successful. Wheel file available at {whl_path}")
return whl_path
def build(root_path):
if os.path.exists(build_directory):
raise ValueError(f"Directory already exists: {build_directory}")
try:
copy_source_to_build_directory(root_path)
run_namex_conversion()
create_legacy_directory()
from keras_core.src.version import __version__ # noqa: E402
export_version_string(__version__)
return build_and_save_output(root_path, __version__)
finally:
# Clean up: remove the build directory (no longer needed)
shutil.rmtree(build_directory)
def install_whl(whl_fpath):
print(f"Installing wheel file: {whl_fpath}")
os.system(f"pip3 install {whl_fpath} --force-reinstall --no-dependencies")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--install",
action="store_true",
help="Whether to install the generated wheel file.",
)
args = parser.parse_args()
root_path = pathlib.Path(__file__).parent.resolve()
whl_path = build(root_path)
if whl_path and args.install:
install_whl(whl_path)