add env setup for runtime to enable multiprocessing parameter search by joblib, closes #23
This commit is contained in:
parent
a1057fc78b
commit
4072b97012
4
pdm.lock
generated
4
pdm.lock
generated
@ -5,7 +5,7 @@
|
|||||||
groups = ["default", "dev", "lint", "nb", "tests"]
|
groups = ["default", "dev", "lint", "nb", "tests"]
|
||||||
strategy = ["inherit_metadata"]
|
strategy = ["inherit_metadata"]
|
||||||
lock_version = "4.5.0"
|
lock_version = "4.5.0"
|
||||||
content_hash = "sha256:545c39ef89d18d28a7bca4b08c93e6fb900c42612089300b867a4e0955acd6ab"
|
content_hash = "sha256:c3fd178d5c4736852fff59e2e4c5e3565b0fb80bf29ec5979e1e9c78d452ee1f"
|
||||||
|
|
||||||
[[metadata.targets]]
|
[[metadata.targets]]
|
||||||
requires_python = ">=3.11"
|
requires_python = ">=3.11"
|
||||||
@ -1623,7 +1623,7 @@ name = "psutil"
|
|||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
requires_python = ">=3.6"
|
requires_python = ">=3.6"
|
||||||
summary = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7."
|
summary = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7."
|
||||||
groups = ["nb"]
|
groups = ["default", "nb"]
|
||||||
files = [
|
files = [
|
||||||
{file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"},
|
{file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"},
|
||||||
{file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"},
|
{file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"},
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "delta-barth"
|
name = "delta-barth"
|
||||||
version = "0.5.8dev11"
|
version = "0.5.8"
|
||||||
description = "workflows and pipelines for the Python-based Plugin of Delta Barth's ERP system"
|
description = "workflows and pipelines for the Python-based Plugin of Delta Barth's ERP system"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Florian Förster", email = "f.foerster@d-opt.com"},
|
{name = "Florian Förster", email = "f.foerster@d-opt.com"},
|
||||||
]
|
]
|
||||||
dependencies = ["scikit-learn>=1.6.1", "pandas>=2.2.3", "xgboost>=2.1.4", "joblib>=1.4.2", "typing-extensions>=4.12.2", "requests>=2.32.3", "pydantic>=2.10.6", "dopt-basics>=0.1.3", "SQLAlchemy>=2.0.39"]
|
dependencies = ["scikit-learn>=1.6.1", "pandas>=2.2.3", "xgboost>=2.1.4", "joblib>=1.4.2", "typing-extensions>=4.12.2", "requests>=2.32.3", "pydantic>=2.10.6", "dopt-basics>=0.1.3", "SQLAlchemy>=2.0.39", "psutil>=7.0.0"]
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = {text = "LicenseRef-Proprietary"}
|
license = {text = "LicenseRef-Proprietary"}
|
||||||
@ -74,7 +74,7 @@ directory = "reports/coverage"
|
|||||||
|
|
||||||
|
|
||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.5.8dev11"
|
current_version = "0.5.8"
|
||||||
parse = """(?x)
|
parse = """(?x)
|
||||||
(?P<major>0|[1-9]\\d*)\\.
|
(?P<major>0|[1-9]\\d*)\\.
|
||||||
(?P<minor>0|[1-9]\\d*)\\.
|
(?P<minor>0|[1-9]\\d*)\\.
|
||||||
|
|||||||
@ -1,23 +1,22 @@
|
|||||||
import multiprocessing
|
# import multiprocessing
|
||||||
import os
|
# import os
|
||||||
import sys
|
# import sys
|
||||||
from pathlib import Path
|
# from pathlib import Path
|
||||||
|
|
||||||
# os.environ["JOBLIB_DISABLE_SHARED_MEMORY"] = "1"
|
# os.environ["JOBLIB_DISABLE_SHARED_MEMORY"] = "1"
|
||||||
|
|
||||||
interpreter = r"A:\Arbeitsaufgaben\Delta-Barth\cs-wrapper\dopt.DeltaBarth\python\python.exe"
|
# interpreter = r"A:\Arbeitsaufgaben\Delta-Barth\cs-wrapper\dopt.DeltaBarth\python\python.exe"
|
||||||
intp_pth = Path(interpreter).resolve()
|
# intp_pth = Path(interpreter).resolve()
|
||||||
|
|
||||||
assert intp_pth.exists(), f"interpreter path seems not to exist: {intp_pth}"
|
# assert intp_pth.exists(), f"interpreter path seems not to exist: {intp_pth}"
|
||||||
|
|
||||||
# multiprocessing.set_executable(str(intp_pth))
|
# multiprocessing.set_executable(str(intp_pth))
|
||||||
# setattr(sys, "frozen", True) # !! causes termination
|
# setattr(sys, "frozen", True) # !! causes termination
|
||||||
# sys.executable = str(intp_pth)
|
# sys.executable = str(intp_pth)
|
||||||
setattr(sys, "executable", str(intp_pth))
|
# setattr(sys, "executable", str(intp_pth))
|
||||||
setattr(sys, "_base_executable", str(intp_pth))
|
# setattr(sys, "_base_executable", str(intp_pth))
|
||||||
# multiprocessing.set_start_method("spawn", force=True)
|
# multiprocessing.set_start_method("spawn", force=True)
|
||||||
|
|
||||||
|
# target = Path(r"A:\Arbeitsaufgaben\Delta-Barth\cs-wrapper\dopt.DeltaBarth")
|
||||||
target = Path(r"A:\Arbeitsaufgaben\Delta-Barth\cs-wrapper\dopt.DeltaBarth")
|
# file = target / "executed.txt"
|
||||||
file = target / "executed.txt"
|
# file.touch()
|
||||||
file.touch()
|
|
||||||
|
|||||||
33
src/delta_barth/_env.py
Normal file
33
src/delta_barth/_env.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
from dopt_basics import io
|
||||||
|
|
||||||
|
PY_RUNTIME_FOLDER: Final[str] = "python"
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_env(
|
||||||
|
lib_path: Path,
|
||||||
|
) -> Path | None:
|
||||||
|
pyrt_folder = io.search_folder_path(
|
||||||
|
starting_path=lib_path,
|
||||||
|
stop_folder_name=PY_RUNTIME_FOLDER,
|
||||||
|
return_inclusive=True,
|
||||||
|
)
|
||||||
|
if pyrt_folder is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
pth_interpreter = pyrt_folder / "python.exe"
|
||||||
|
if not pth_interpreter.exists():
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"dopt-delta-barth seems to be deployed in a standalone runtime, "
|
||||||
|
f"but the interpreter was not found under: {pth_interpreter}"
|
||||||
|
)
|
||||||
|
|
||||||
|
setattr(sys, "executable", str(pth_interpreter))
|
||||||
|
setattr(sys, "_base_executable", str(pth_interpreter))
|
||||||
|
|
||||||
|
return pyrt_folder
|
||||||
@ -38,6 +38,7 @@ from delta_barth.constants import (
|
|||||||
DEFAULT_DB_ERR_CODE,
|
DEFAULT_DB_ERR_CODE,
|
||||||
DUMMY_DATA_PATH,
|
DUMMY_DATA_PATH,
|
||||||
FEATURES_SALES_PROGNOSIS,
|
FEATURES_SALES_PROGNOSIS,
|
||||||
|
MAX_NUM_WORKERS,
|
||||||
SALES_MIN_NUM_DATAPOINTS,
|
SALES_MIN_NUM_DATAPOINTS,
|
||||||
)
|
)
|
||||||
from delta_barth.errors import STATUS_HANDLER, wrap_result
|
from delta_barth.errors import STATUS_HANDLER, wrap_result
|
||||||
@ -302,7 +303,7 @@ def _process_sales(
|
|||||||
params,
|
params,
|
||||||
scoring="neg_mean_absolute_error",
|
scoring="neg_mean_absolute_error",
|
||||||
cv=kfold,
|
cv=kfold,
|
||||||
n_jobs=-1,
|
n_jobs=MAX_NUM_WORKERS,
|
||||||
n_iter=100,
|
n_iter=100,
|
||||||
verbose=0,
|
verbose=0,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -430,6 +430,7 @@ def test_export_on_fail():
|
|||||||
assert res.status.description == status.description
|
assert res.status.description == status.description
|
||||||
|
|
||||||
|
|
||||||
|
@patch("delta_barth.session.CFG_HOT_RELOAD", False)
|
||||||
def test_pipeline_sales_forecast_SuccessDbWrite(exmpl_api_sales_prognosis_resp, session):
|
def test_pipeline_sales_forecast_SuccessDbWrite(exmpl_api_sales_prognosis_resp, session):
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
|
|||||||
49
tests/test_env.py
Normal file
49
tests/test_env.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import importlib
|
||||||
|
import sys
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import delta_barth.constants
|
||||||
|
from delta_barth import _env
|
||||||
|
|
||||||
|
|
||||||
|
@patch("delta_barth._env.PY_RUNTIME_FOLDER", "test123456")
|
||||||
|
def test_prepare_env_NoRuntimeFolder(tmp_path):
|
||||||
|
ret = _env.prepare_env(tmp_path)
|
||||||
|
assert ret is None
|
||||||
|
|
||||||
|
|
||||||
|
@patch("delta_barth._env.PY_RUNTIME_FOLDER", "base")
|
||||||
|
def test_prepare_env_FailNoInterpreter(tmp_path_factory):
|
||||||
|
mocked_lib_pth = tmp_path_factory.mktemp("path") / "to/base/folder/lib/"
|
||||||
|
mocked_lib_pth.mkdir(parents=True, exist_ok=True)
|
||||||
|
with pytest.raises(FileNotFoundError):
|
||||||
|
_ = _env.prepare_env(mocked_lib_pth)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("delta_barth._env.PY_RUNTIME_FOLDER", "base")
|
||||||
|
def test_prepare_env_Success(tmp_path_factory):
|
||||||
|
mocked_lib_pth = tmp_path_factory.mktemp("path") / "to/base/folder/lib/"
|
||||||
|
mocked_lib_pth.mkdir(parents=True, exist_ok=True)
|
||||||
|
rt_path = mocked_lib_pth.parents[1]
|
||||||
|
mocked_interpreter = rt_path / "python.exe"
|
||||||
|
mocked_interpreter.touch()
|
||||||
|
assert mocked_interpreter.exists()
|
||||||
|
ret = _env.prepare_env(mocked_lib_pth)
|
||||||
|
assert ret == rt_path
|
||||||
|
# sys attributes
|
||||||
|
executable = getattr(sys, "executable")
|
||||||
|
assert executable == str(mocked_interpreter)
|
||||||
|
base_executable = getattr(sys, "_base_executable")
|
||||||
|
assert base_executable == str(mocked_interpreter)
|
||||||
|
|
||||||
|
class MockPath:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.parent = mocked_lib_pth
|
||||||
|
|
||||||
|
with patch("pathlib.Path", MockPath):
|
||||||
|
(mocked_lib_pth / "_dummy_data").mkdir(exist_ok=True)
|
||||||
|
importlib.reload(delta_barth.constants)
|
||||||
|
assert delta_barth.constants.DEPLOYMENT_STATUS
|
||||||
|
assert delta_barth.constants.RUNTIME_PATH == rt_path
|
||||||
@ -45,6 +45,7 @@ def test_write_performance_metrics_FailStartingTime(session):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("delta_barth.session.CFG_HOT_RELOAD", False)
|
||||||
def test_sales_prognosis_pipeline(exmpl_api_sales_prognosis_resp, session, monkeypatch):
|
def test_sales_prognosis_pipeline(exmpl_api_sales_prognosis_resp, session, monkeypatch):
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
|
import tomllib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import tomli_w
|
||||||
|
|
||||||
|
import delta_barth.config
|
||||||
import delta_barth.session
|
import delta_barth.session
|
||||||
from delta_barth import logging
|
from delta_barth import logging
|
||||||
from delta_barth.constants import (
|
from delta_barth.constants import (
|
||||||
DEFAULT_API_ERR_CODE,
|
DEFAULT_API_ERR_CODE,
|
||||||
HTTP_BASE_CONTENT_HEADERS,
|
HTTP_BASE_CONTENT_HEADERS,
|
||||||
LOG_FILENAME,
|
|
||||||
)
|
)
|
||||||
|
from delta_barth.logging import LOG_FILENAME
|
||||||
|
|
||||||
|
|
||||||
def test_validate_path_Success():
|
def test_validate_path_Success():
|
||||||
@ -62,7 +65,7 @@ def test_session_setup_db_management(tmp_path):
|
|||||||
assert db_path.exists()
|
assert db_path.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_session_setup_config(tmp_path, pth_cfg):
|
def test_session_setup_config(tmp_path):
|
||||||
str_path = str(tmp_path)
|
str_path = str(tmp_path)
|
||||||
foldername: str = "cfg_test"
|
foldername: str = "cfg_test"
|
||||||
target_cfg_dir = tmp_path / foldername
|
target_cfg_dir = tmp_path / foldername
|
||||||
@ -80,6 +83,61 @@ def test_session_setup_config(tmp_path, pth_cfg):
|
|||||||
assert session.cfg.forecast.threshold_month_data_points == 28
|
assert session.cfg.forecast.threshold_month_data_points == 28
|
||||||
|
|
||||||
|
|
||||||
|
@patch("delta_barth.session.CFG_HOT_RELOAD", False)
|
||||||
|
def test_session_reload_config_NoHotReload(tmp_path):
|
||||||
|
str_path = str(tmp_path)
|
||||||
|
foldername: str = "cfg_test"
|
||||||
|
target_cfg_dir = tmp_path / foldername
|
||||||
|
session = delta_barth.session.Session(HTTP_BASE_CONTENT_HEADERS, cfg_folder=foldername)
|
||||||
|
session.set_data_path(str_path)
|
||||||
|
cfg_path = session.cfg_path
|
||||||
|
assert cfg_path.parent.exists()
|
||||||
|
assert cfg_path.parent == target_cfg_dir
|
||||||
|
assert not cfg_path.exists()
|
||||||
|
session.setup()
|
||||||
|
assert cfg_path.exists()
|
||||||
|
parsed_cfg = session.cfg
|
||||||
|
assert isinstance(parsed_cfg, delta_barth.config.Config)
|
||||||
|
# modify config and reload
|
||||||
|
with open(cfg_path, "rb") as file:
|
||||||
|
cfg_data = tomllib.load(file)
|
||||||
|
cfg_data["forecast"]["threshold_month_data_points"] = 30
|
||||||
|
with open(cfg_path, "wb") as file:
|
||||||
|
tomli_w.dump(cfg_data, file)
|
||||||
|
|
||||||
|
assert session.cfg.forecast.threshold_month_data_points == 28
|
||||||
|
|
||||||
|
session.reload_cfg()
|
||||||
|
reload_cfg = session.cfg
|
||||||
|
assert isinstance(reload_cfg, delta_barth.config.Config)
|
||||||
|
assert reload_cfg.forecast.threshold_month_data_points == 30
|
||||||
|
|
||||||
|
|
||||||
|
@patch("delta_barth.session.CFG_HOT_RELOAD", True)
|
||||||
|
def test_session_reload_config_HotReload(tmp_path):
|
||||||
|
str_path = str(tmp_path)
|
||||||
|
foldername: str = "cfg_test"
|
||||||
|
target_cfg_dir = tmp_path / foldername
|
||||||
|
session = delta_barth.session.Session(HTTP_BASE_CONTENT_HEADERS, cfg_folder=foldername)
|
||||||
|
session.set_data_path(str_path)
|
||||||
|
cfg_path = session.cfg_path
|
||||||
|
assert cfg_path.parent.exists()
|
||||||
|
assert cfg_path.parent == target_cfg_dir
|
||||||
|
assert not cfg_path.exists()
|
||||||
|
session.setup()
|
||||||
|
assert cfg_path.exists()
|
||||||
|
parsed_cfg = session.cfg
|
||||||
|
assert isinstance(parsed_cfg, delta_barth.config.Config)
|
||||||
|
# modify config and reload
|
||||||
|
with open(cfg_path, "rb") as file:
|
||||||
|
cfg_data = tomllib.load(file)
|
||||||
|
cfg_data["forecast"]["threshold_month_data_points"] = 30
|
||||||
|
with open(cfg_path, "wb") as file:
|
||||||
|
tomli_w.dump(cfg_data, file)
|
||||||
|
|
||||||
|
assert session.cfg.forecast.threshold_month_data_points == 30
|
||||||
|
|
||||||
|
|
||||||
@patch("delta_barth.logging.ENABLE_LOGGING", True)
|
@patch("delta_barth.logging.ENABLE_LOGGING", True)
|
||||||
@patch("delta_barth.logging.LOGGING_TO_FILE", True)
|
@patch("delta_barth.logging.LOGGING_TO_FILE", True)
|
||||||
@patch("delta_barth.logging.LOGGING_TO_STDERR", True)
|
@patch("delta_barth.logging.LOGGING_TO_STDERR", True)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user