Skip to content

Commit 4153484

Browse files
Merge pull request #5 from KhiopsML/1-package-khisto-python
1 package khisto python
2 parents 909e9ec + ddf8fc6 commit 4153484

22 files changed

Lines changed: 443 additions & 18 deletions

.github/workflows/pack-pip.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: pip packaging
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
python_repository:
6+
description: python repository
7+
type: choice
8+
default: None # no publishing to any Package Index by default
9+
options: [None, testpypi, pypi]
10+
push:
11+
tags: ['*']
12+
pull_request:
13+
paths:
14+
- .github/workflows/pack-pip.yml
15+
- pyproject.toml
16+
- CMakeLists.txt
17+
18+
defaults:
19+
run:
20+
shell: bash
21+
22+
jobs:
23+
build-wheel:
24+
name: Build wheel
25+
runs-on: ${{ matrix.os }}
26+
strategy:
27+
matrix:
28+
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-2025, windows-11-arm, macos-15-intel, macos-15]
29+
steps:
30+
- uses: actions/checkout@v6
31+
32+
- name: Load Visual C++ Environment Variables (Windows)
33+
if: runner.os == 'Windows'
34+
shell: cmd
35+
run: |
36+
call "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat"
37+
set >> %GITHUB_ENV%
38+
39+
- name: Build wheels
40+
uses: pypa/cibuildwheel@v3.3.1
41+
42+
- name: install Python
43+
uses: actions/setup-python@v5
44+
45+
- name: Test built wheel
46+
run: |
47+
pip install --find-links=wheelhouse khisto
48+
pip install pytest>=8.3 pytest-cov>=6 pytest-xdist>=3.6 pytest-sugar>=1.0
49+
pytest tests/
50+
51+
- uses: actions/upload-artifact@v6
52+
with:
53+
name: pkg-wheel-${{ matrix.os }}
54+
path: wheelhouse/*.whl
55+
if-no-files-found: error
56+
57+
release-testpypi:
58+
# Publish only on tag pushes in the KhiopsML repository
59+
if: github.ref_type == 'tag' && github.repository_owner == 'KhiopsML' && inputs.python_repository == 'testpypi'
60+
name: Publish to Test.PyPI.org
61+
needs: [build-wheel]
62+
runs-on: ubuntu-latest
63+
permissions:
64+
id-token: write
65+
environment:
66+
name: testpypi
67+
steps:
68+
- uses: actions/download-artifact@v5
69+
with:
70+
pattern: pkg-*
71+
path: dist
72+
merge-multiple: true
73+
- uses: pypa/gh-action-pypi-publish@release/v1
74+
with:
75+
verbose: true
76+
repository-url: https://test.pypi.org/legacy/
77+
78+
release-pypi:
79+
# Publish only on tag pushes in the KhiopsML repository
80+
name: Publish to PyPI.org
81+
if: github.ref_type == 'tag' && github.repository_owner == 'KhiopsML' && inputs.python_repository == 'pypi'
82+
needs: [build-wheel]
83+
runs-on: ubuntu-latest
84+
permissions:
85+
id-token: write
86+
environment:
87+
name: pypi
88+
steps:
89+
- uses: actions/download-artifact@v5
90+
with:
91+
pattern: pkg-*
92+
path: dist
93+
merge-multiple: true
94+
- uses: pypa/gh-action-pypi-publish@release/v1
95+
with:
96+
verbose: true

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,6 @@ uv.lock
177177

178178
# Excel
179179
**/*.xlsx
180+
181+
# macOS
182+
.DS_Store

.gitmodules

Lines changed: 0 additions & 3 deletions
This file was deleted.

.pre-commit-config.yaml

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,12 @@ repos:
112112
pass_filenames: false
113113
always_run: true
114114
args: [--markdown-linebreak-ext=md]
115-
- id: pyrefly
116-
name: pyrefly
117-
entry: pyrefly check
118-
language: system
119-
pass_filenames: false
120-
always_run: true
115+
# - id: pyrefly
116+
# name: pyrefly
117+
# entry: pyrefly check
118+
# language: system
119+
# pass_filenames: false
120+
# always_run: true
121121
- id: ruff-check
122122
name: ruff-check
123123
entry: ruff check
@@ -130,3 +130,12 @@ repos:
130130
entry: ruff format
131131
language: system
132132
pass_filenames: false
133+
- id: update-copyright
134+
name: update-copyright
135+
entry: python scripts/update-copyright.py
136+
language: system
137+
types_or: [c, c++, java, python]
138+
- id: check-obsolete-copyright
139+
name: check-obsolete-copyright
140+
entry: python scripts/check-obsolete-copyright.py
141+
language: system

CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cmake_minimum_required(VERSION 3.16.3)
2+
project(khisto)
3+
include(FetchContent)
4+
5+
# Extract the khiops version from pyproject.toml
6+
# The version is specified in the "tool.khiops" section as "khiops-version = "x.y.z""
7+
# It must correspond to a tag in the khiops repository
8+
file(READ pyproject.toml PYPROJECT_FILE)
9+
string(REGEX MATCH "khiops-version = \"([^\"]*)\"" _ ${PYPROJECT_FILE})
10+
set(KHIOPS_VERSION ${CMAKE_MATCH_1})
11+
if(NOT KHIOPS_VERSION)
12+
message(FATAL_ERROR "Could not extract khiops version from pyproject.toml")
13+
endif()
14+
message(STATUS "Khiops version: ${KHIOPS_VERSION}")
15+
16+
# Download and build khiops from the specified tag
17+
fetchcontent_Declare(
18+
khiops
19+
GIT_REPOSITORY https://github.com/KhiopsML/khiops.git
20+
GIT_TAG ${KHIOPS_VERSION})
21+
FetchContent_MakeAvailable(khiops)

LICENSE

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
The Clear BSD License
2+
3+
Copyright (c) 2023-2026 Orange S.A.
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted (subject to the limitations in the disclaimer
8+
below) provided that the following conditions are met:
9+
10+
* Redistributions of source code must retain the above copyright notice,
11+
this list of conditions and the following disclaimer.
12+
13+
* Redistributions in binary form must reproduce the above copyright
14+
notice, this list of conditions and the following disclaimer in the
15+
documentation and/or other materials provided with the distribution.
16+
17+
* Neither the name of the copyright holder nor the names of its
18+
contributors may be used to endorse or promote products derived from this
19+
software without specific prior written permission.
20+
21+
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
22+
THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23+
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30+
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32+
POSSIBILITY OF SUCH DAMAGE.

pyproject.toml

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@ name = "khisto"
33
version = "0.1.0"
44
description = "Optimal histogram visualization using the Khiops algorithm"
55
readme = "README.md"
6-
requires-python = ">=3.11"
6+
license = "BSD-3-Clause-Clear"
7+
license-files = ["LICENSE"]
8+
requires-python = ">=3.10"
79
dependencies = ["numpy>=2.0"]
810

11+
[tool.khiops]
12+
khiops-version = "11.0.1-a.2"
13+
914
[project.optional-dependencies]
1015
matplotlib = ["matplotlib>=3.8"]
1116
all = ["matplotlib>=3.8"]
1217

13-
1418
[dependency-groups]
1519
dev = [
1620
"pytest>=8.3",
@@ -25,8 +29,8 @@ dev = [
2529
]
2630

2731
[build-system]
28-
requires = ["hatchling>=1.25"]
29-
build-backend = "hatchling.build"
32+
requires = ["scikit-build-core>=0.11.6", "ninja"]
33+
build-backend = "scikit_build_core.build"
3034

3135
[tool.pytest.ini_options]
3236
addopts = "-n 4 --cov --cov-report term-missing --durations=10"
@@ -42,7 +46,6 @@ ignore-regex = 'https://([\w/\.])+'
4246
quiet-level = 3
4347

4448
[tool.ruff]
45-
4649
# Group violations by containing file.
4750
output-format = "grouped"
4851
line-length = 88
@@ -52,7 +55,7 @@ indent-width = 4
5255
fix = true
5356

5457
# Exclude a variety of commonly ignored directories.
55-
extend-exclude = ["src/khiops"]
58+
extend-exclude = ["scripts", "docs", "sandbox", ".github"]
5659

5760
[tool.ruff.format]
5861
# Like Black, use double quotes for strings.
@@ -70,5 +73,33 @@ ignore = [
7073
"E402", # module level import not at top of file
7174
]
7275

73-
[tool.pyrefly]
74-
project-excludes = ["src/khiops"]
76+
[tool.scikit-build]
77+
wheel.py-api = "py3" # We specify full compatibility with all Python 3 releases
78+
cmake.version = "CMakeLists.txt" # Read CMake minimal version from file
79+
cmake.args = ["-G", "Ninja"] # Generator specification and build configuration
80+
81+
# CMake variables
82+
# Build khisto binary in Release mode for better performance, as it is not intended for debugging
83+
cmake.define.CMAKE_BUILD_TYPE = "Release"
84+
# Options
85+
cmake.define.MPI = false
86+
cmake.define.TESTING = false
87+
cmake.define.BUILD_LEX_YACC = false
88+
cmake.define.BUILD_JARS = false
89+
cmake.define.GENERATE_VIEWS = false
90+
cmake.define.C11 = true
91+
ninja.make-fallback = false
92+
build.targets = ["khisto"] # Build only khisto
93+
install.components = ["KHISTO"] # Only install KHISTO component in wheel
94+
95+
[tool.cibuildwheel]
96+
build = "cp310-*"
97+
skip = "*musllinux*" # Khiops does not compile when using musl as the C standard library
98+
archs = "auto64" # Do not build 32-bit wheels
99+
100+
# Install build-time dependencies in containers created by cibuildwheel
101+
# This is compatible with Debian-like and Fedora-like distributions
102+
[tool.cibuildwheel.linux]
103+
before-all = [
104+
"apt-get install -y ninja-build || dnf install -y ninja-build"
105+
]
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright (c) 2025-2026 Orange. All rights reserved.
2+
# This software is distributed under the BSD 3-Clause-clear License, the text of which is available
3+
# at https://spdx.org/licenses/BSD-3-Clause-Clear.html or see the "LICENSE" file for more details.
4+
5+
"""Updates the copyright notice of the input files"""
6+
7+
import argparse
8+
import os
9+
import sys
10+
from datetime import datetime
11+
12+
# Constants to check the presence of obsolete copyright
13+
copyright_prefix = bytes("(c) 2025-", encoding="ascii")
14+
valid_copyright = bytes(f"(c) 2025-{datetime.today().year} Orange", encoding="ascii")
15+
16+
# List of special files
17+
special_files = ["LICENSE"]
18+
19+
20+
def main(args):
21+
"""Main method"""
22+
# Check obsolete copyright
23+
valid_copyright = True
24+
for file_path in args.file_paths:
25+
if test_obsolete_copyright(file_path):
26+
print(f"Copyright notice must be updated in {file_path}")
27+
valid_copyright = False
28+
29+
# Set the return code
30+
return_code = 0
31+
if not valid_copyright:
32+
return_code = 1
33+
34+
return return_code
35+
36+
37+
def test_obsolete_copyright(file_path):
38+
"""Check if a special file contains an obsolete copyright notice"""
39+
40+
basename = os.path.basename(file_path)
41+
if basename in special_files:
42+
# Read the lines from the source file
43+
with open(file_path, "rb") as file:
44+
lines = file.readlines()
45+
46+
# Check copyright
47+
for n, line in enumerate(lines):
48+
line = line.rstrip()
49+
if copyright_prefix in line and valid_copyright not in line:
50+
return True
51+
# Obsolete copyright not found
52+
return False
53+
54+
55+
if __name__ == "__main__":
56+
parser = argparse.ArgumentParser(
57+
prog="python check-obsolete-copyright.py",
58+
formatter_class=argparse.RawTextHelpFormatter,
59+
description="Checks the presence of an obsolete copyright notice of the input files",
60+
)
61+
parser.add_argument(
62+
"file_paths",
63+
metavar="FILE",
64+
nargs="+",
65+
help="One or more source files",
66+
)
67+
sys.exit(main(parser.parse_args()))

0 commit comments

Comments
 (0)