Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create pypi wheel for opm-python #6

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions pypi/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Python scripts for generating and uploading OPM Python packages to PyPI

## Installation of the python scripts
- Requires python3 >= 3.10

### Using poetry
For development it is recommended to use poetry:

- Install [poetry](https://python-poetry.org/docs/)
- Then run:
```
$ poetry install
$ poetry shell
```

### Installation into virtual environment
If you do not plan to change the code, you can do a regular installation into a VENV:

```
$ python -m venv .venv
$ source .venv/bin/activate
$ pip install .
```
123 changes: 123 additions & 0 deletions pypi/scripts/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions pypi/scripts/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[tool.poetry]
name = "opm-pypi-tools"
version = "0.1.0"
description = """Helper scripts for generating and uploading OPM Python packages to PyPI."""
authors = ["Håkon Hægland <[email protected]>"]
readme = "README.md"
packages = [{ include = "opm_pypi_tools", from = "src"}]
license = "GPL3"
repository = "https://github.com/OPM/opm-python"

[tool.poetry.dependencies]
python = "^3.10"
click = "^8.1.7"
build = "^1.2.1"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.scripts]
opm-pypi-tools = "opm_pypi_tools.main:main"
152 changes: 152 additions & 0 deletions pypi/scripts/src/opm_pypi_tools/build_dist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import logging
import os
import shutil
import subprocess
import sys

from pathlib import Path

from opm_pypi_tools.constants import Directories, DockerImageName
from opm_pypi_tools.docker import DockerManager
from opm_pypi_tools.helpers import Helpers

class BuildDist:
def __init__(
self,
build_dir: str,
source_dir: str,
python_package_version: str,
opm_version: str,
output_dir: str,
docker_image: DockerImageName|None
) -> None:
self.build_dir = Path(build_dir).resolve()
self.source_dir = Path(source_dir).resolve()
self.template_dir = self.source_dir / Directories.pypi / Directories.templates
self.opm_version = opm_version + python_package_version
self.project_dir = self.build_dir / Directories.opm_python
if not self.project_dir.exists():
raise FileNotFoundError(f"Directory {self.project_dir} does not exist.")
self.output_dir = Path(output_dir).resolve()
self.docker_image = docker_image
self.src_dir = self.project_dir / Directories.source

def build(self):
self.copy_source_files()
self.opm_common_so_fn = Helpers.find_opm_common_so(self.output_dir)
self.opm_simulators_so_fn = Helpers.find_opm_simulators_so(self.output_dir)
self.generate_setup_py()
self.generate_pyproject_toml()
self.generate_readme()
self.generate_license()
self.generate_manifest()
if self.docker_image is None:
self.run_build_py()
else:
self.generate_dockerfile()
self.generate_docker_build_script()
self.maybe_build_docker_image()
self.run_build_in_container()
return

def copy_file_to_output_dir(self, filename: str):
logging.info(f"Copying {filename} to {self.output_dir}...")
shutil.copy(self.template_dir / filename, self.output_dir / filename)
logging.info(f"{filename} copied to {self.output_dir}.")
return

def copy_source_files(self):
logging.info(f"Copying source files from {self.src_dir} to {self.output_dir}...")
# We can assume that the source directory exists and that the output directory does not exist
# This will copy the content of the source directory to the output directory,
# (but not the source directory itself)
shutil.copytree(self.src_dir, self.output_dir)
logging.info(f"Source files copied to {self.output_dir}.")
return

def generate_dockerfile(self):
logging.info(f"Generating Dockerfile in {self.output_dir}...")
content = Helpers.read_template_file(
self.template_dir / "Dockerfile",
variables={
"docker_image_name": self.docker_image.name
}
)
with open(self.output_dir / "Dockerfile", "w", encoding='utf-8') as file:
file.write(content)
logging.info(f"Dockerfile generated in {self.output_dir}.")
return

def generate_docker_build_script(self):
self.copy_file_to_output_dir("manylinux-build.sh")
return

def generate_license(self):
self.copy_file_to_output_dir("LICENSE")
return

def generate_manifest(self):
self.copy_file_to_output_dir("MANIFEST.in")
return

def generate_pyproject_toml(self):
logging.info(f"Generating pyproject.toml in {self.output_dir}...")
content = Helpers.read_template_file(
self.template_dir / "pyproject.toml",
variables={
"opm_common_so": self.opm_common_so_fn,
"opm_simulators_so": self.opm_simulators_so_fn,
"package_version": self.opm_version,
}
)
with open(self.output_dir / "pyproject.toml", "w", encoding='utf-8') as file:
file.write(content)
logging.info(f"pyproject.toml generated in {self.output_dir}.")

def generate_readme(self):
self.copy_file_to_output_dir("README.md")
return

def generate_setup_py(self):
self.copy_file_to_output_dir("setup.py")
return

def maybe_build_docker_image(self):
self.docker_manager = DockerManager(self.docker_image, self.output_dir)
self.docker_manager.build()
return

def run_build_in_container(self):
#for python_version in ["cp38-cp38", "cp39-cp39", "cp310-cp310", "cp311-cp311", "cp312-cp312"]:
for python_version in ["cp311-cp311"]:
self.docker_manager.run_build_in_container(python_version)
return

def run_build_py(self):
# Run "WHEEL_PLAT_NAME=manylinux2014_x86_64 python3 -m build --sdist --wheel"
env = os.environ.copy()
env["WHEEL_PLAT_NAME"] = "manylinux2014_x86_64"
try:
result = subprocess.run(
[
sys.executable, # Use the same python3 interpreter that is running this script
"-m",
"build",
"--sdist",
"--wheel",
],
cwd=self.output_dir,
check=True, # Raise an exception if the process returns a non-zero exit code
env=env
)
logging.info(f"'python -m build ..' ran successfully in {self.output_dir}.")
except subprocess.CalledProcessError as e:
logging.error(f"Error running build.py: {e}")
sys.exit(1)
except FileNotFoundError:
print("Error: build.py not found.")
sys.exit(1)
except Exception as e:
logging.error(f"Error running build.py: {e}")
sys.exit(1)
return
16 changes: 16 additions & 0 deletions pypi/scripts/src/opm_pypi_tools/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import enum

class Directories:
docs = "docs"
opm = "opm"
opm_python = "opm-python"
pypi = "pypi"
simulators = "simulators"
source = "src"
templates = "templates"

class DockerImageName(enum.Enum):
manylinux2014_x86_64 = 0

class Filenames:
readme = "README.md"
Loading