diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b45a8..5ccf5a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [v0.9.0-alpha] - 2024.09.09 +## [v0.9.0-beta] - 2024.09.10 ### Added + Platform (e.g. Windows, Mac, Linux) and architecture (e.g. x86_64, arm64) specific private source environments and evaluate.py ++ New `utils/private_src_utils.py` module that provides helper functions for managing obfuscated code imports ### Fixed ++ Conditional statements for importing LBG1_LG3 environments so that the lighter-weight install of kspdg without `juliacall` dependency does not error on import + ### Changed + Updating environment READMEs for accuracy @@ -20,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed ++ Removed unused astropy imports in `utils.py` + ## [v0.8.1] - 2024.08.14 ### Added diff --git a/src/kspdg/__init__.py b/src/kspdg/__init__.py index 19cfadd..f0ded74 100644 --- a/src/kspdg/__init__.py +++ b/src/kspdg/__init__.py @@ -5,7 +5,7 @@ # Single-sourcing package version # https://packaging.python.org/guides/single-sourcing-package-version/ -__version__ = "0.9.0-alpha" +__version__ = "0.9.0-beta" # these imports make the individual environments accessible at the top-level # of the library and assign an environment version number @@ -41,78 +41,38 @@ from kspdg.sb1.e1_envs import SB1_E1_I3_Env as SB1_E1_I3_V1 from kspdg.sb1.e1_envs import SB1_E1_I4_Env as SB1_E1_I4_V1 from kspdg.sb1.e1_envs import SB1_E1_I5_Env as SB1_E1_I5_V1 + -# Private-source, python-version-specific, platform and architecture-specific -# environments with advanced bots (e.g. julia-based) -import sys -import platform +# import functions but name-mangle them so they are not +# inadvertently top-level accessible in the kspdg package import importlib -def get_python_version(): - # Get the Python version in the format 'python3_12' - version_info = sys.version_info - return f"python{version_info.major}_{version_info.minor}" - -def get_platform_architecture(): - # Get the platform and architecture information - system_platform = platform.system() - machine = platform.machine() - - if system_platform == 'Darwin': - system_platform = 'Darwin' - elif system_platform == 'Linux': - system_platform = 'Linux' - elif system_platform == 'Windows': - system_platform = 'Windows' - else: - raise RuntimeError(f"Unsupported platform: {system_platform}") - - # Handle architecture - if machine == 'x86_64': - architecture = 'x86_64' - elif machine in ['arm64', 'armv8']: - architecture = 'arm64' - else: - raise RuntimeError(f"Unsupported architecture: {machine}") +from importlib.util import find_spec +from kspdg.utils.private_src_utils import get_private_src_module_str as __get_mod_str - return f"{system_platform}_{architecture}" - -def get_private_src_module_str(mod_name): - """returns string of module location to obfuscated code based on python version and system architecture""" - # Get dynamic parts of the import path - python_version = get_python_version() - platform_architecture = get_platform_architecture() - - # Build the module path - return f"kspdg.private_src.{python_version}.{platform_architecture}.{mod_name}" - - -# import obfuscated LBG1-LG3 environments -__lg3_envs_path = get_private_src_module_str("kspdg_envs.lbg1.lg3_envs") -try: - __lg3_envs_module = importlib.import_module(__lg3_envs_path) -except ModuleNotFoundError: - print(f"Module {__lg3_envs_path} not found.") -LBG1_LG3_I1_V1 = getattr(__lg3_envs_module, 'LBG1_LG3_I1_Env') -LBG1_LG3_I2_V1 = getattr(__lg3_envs_module, 'LBG1_LG3_I2_Env') # import obfuscated evaluate.py -__evaluate_path = get_private_src_module_str("kspdg_envs.dist_evaluate") +__evaluate_path = __get_mod_str("kspdg_envs.dist_evaluate") try: evaluate = importlib.import_module(__evaluate_path) except ModuleNotFoundError: print(f"Module {__evaluate_path} not found.") - -# current_platform = platform.system() -# architecture = platform.machine() -# platform_architecture = f"{current_platform}_{architecture}" -# if sys.version_info[:2] == (3, 12): -# # Python 3.12a -# from kspdg.private_src.python3_12.kspdg_envs.lbg1.lg3_envs import LBG1_LG3_I1_Env as LBG1_LG3_I1_V1 -# from kspdg.private_src.python3_12.kspdg_envs.lbg1.lg3_envs import LBG1_LG3_I2_Env as LBG1_LG3_I2_V1 -# elif sys.version_info[:2] == (3, 9): -# # Python 3.9 -# from kspdg.private_src.python3_9.kspdg_envs.lbg1.lg3_envs import LBG1_LG3_I1_Env as LBG1_LG3_I1_V1 -# from kspdg.private_src.python3_9.kspdg_envs.lbg1.lg3_envs import LBG1_LG3_I2_Env as LBG1_LG3_I2_V1 -# else: -# # Handle other versions or raise an error -# raise ImportError(f"Private-source environments require python 3.9 or 3.12, got {sys.version}") + +# Condition import of LG3 environments on the presence of juliacall dependency +# Therefore, the kspdg library should be usable without the adv_bots optional +# dependency +if find_spec('juliacall') is not None: + # import obfuscated LBG1-LG3 environments + __lg3_envs_path = __get_mod_str("kspdg_envs.lbg1.lg3_envs") + try: + __lg3_envs_module = importlib.import_module(__lg3_envs_path) + except ModuleNotFoundError: + print(f"Module {__lg3_envs_path} not found.") + LBG1_LG3_I1_V1 = getattr(__lg3_envs_module, 'LBG1_LG3_I1_Env') + LBG1_LG3_I2_V1 = getattr(__lg3_envs_module, 'LBG1_LG3_I2_Env') + +else: + LBG1_LG3_I1_V1 = LBG1_LG3_I2_V1 = lambda *args, **kwargs: ( + "Unmet dependency juliacall for using LBG1_LG3 environments.\n" + + "Please install kspdg[adv_bots] or kspdg[full] to use these.\n" + + "Refere to README for further instructions." + ) diff --git a/src/kspdg/utils/private_src_utils.py b/src/kspdg/utils/private_src_utils.py new file mode 100644 index 0000000..0924e38 --- /dev/null +++ b/src/kspdg/utils/private_src_utils.py @@ -0,0 +1,54 @@ +# Copyright (c) 2024, MASSACHUSETTS INSTITUTE OF TECHNOLOGY +# Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014). +# SPDX-License-Identifier: MIT + +# Functions that help with management and imports of obfuscated code ("private source" code) + +# Private-source, python-version-specific, platform and architecture-specific +# environments with advanced bots (e.g. julia-based) +import sys +import platform + +def get_python_version(): + # Get the Python version in the format 'python3_12' + version_info = sys.version_info + return f"python{version_info.major}_{version_info.minor}" + +def get_supported_architecture(): + """Checks and returns architecture of user's machine while checking for compatibility""" + + # Define supported architectures + supported_architectures = [ + 'Darwin_x86_64', # MacOS w/ Intel + 'Darwin_arm64', # MacOS w/ Apple Silicon + 'Windows_x86_64', # Windows w/ Intel (confusingly Windows call this AMD64) + 'Linux_x86_64' # Linux w/ Intel + ] + + # Get the platform and architecture of user's machine + platform_os = platform.system() + machine = platform.machine() + + # Handle Windows architecture naming convention + if platform_os == 'Windows' and machine == 'AMD64': + # Rename architecture to align with pyarmor platform options to + # handle Windows architecture naming convention + # that uses x86_64 and AMD64 synonomously + machine = 'x86_64' + + # compose architecture string and check for compatibility w/ + # obfuscated code from pyarmor + architecture = f"{platform_os}_{machine}" + if architecture not in supported_architectures: + raise RuntimeError(f"Unsupported architecute: {architecture}") + + return architecture + +def get_private_src_module_str(mod_name): + """returns string of module location to obfuscated code based on python version and system architecture""" + # Get dynamic parts of the import path + python_version = get_python_version() + platform_architecture = get_supported_architecture() + + # Build the module path + return f"kspdg.private_src.{python_version}.{platform_architecture}.{mod_name}" \ No newline at end of file diff --git a/src/kspdg/utils/utils.py b/src/kspdg/utils/utils.py index 4cad170..3f87bef 100644 --- a/src/kspdg/utils/utils.py +++ b/src/kspdg/utils/utils.py @@ -4,13 +4,9 @@ import numpy as np -import astropy.units as astro_units - from copy import deepcopy from typing import List -from astropy.units import Quantity - from kspdg.utils import constants as CONST def convert_lhcbci_to_rhcbci(v__lhcbci: List[float]) -> List[float]: diff --git a/tests/serverless_tests/test_jl_solvers.py b/tests/serverless_tests/test_jl_solvers.py index af6deef..9ca480d 100644 --- a/tests/serverless_tests/test_jl_solvers.py +++ b/tests/serverless_tests/test_jl_solvers.py @@ -1,24 +1,15 @@ # krpc-serverless pytests for differential game solvers written in julia import pytest -import sys import numpy as np from pathlib import Path +from kspdg.utils.private_src_utils import get_private_src_module_str + THIS_FILE = Path(__file__) -# if sys.version_info[:2] == (3, 12): -# # Python 3.12 -# SOLVE_LBG1_JL_PATH= Path(THIS_FILE.parent, "../../src/kspdg/private_src/python3_12/kspdg_envs/lbg1/solve_lbg1.jl") -# elif sys.version_info[:2] == (3, 9): -# # Python 3.9 -# SOLVE_LBG1_JL_PATH= Path(THIS_FILE.parent, "../../src/kspdg/private_src/python3_9/kspdg_envs/lbg1/solve_lbg1.jl") -# else: -# # Handle other versions or raise an error -# raise ImportError(f"solve_lbg1.jl requires python 3.9 or 3.12, got {sys.version}") - -import kspdg -SOLVE_LBG1_JL_PATH = kspdg.get_private_src_module_str("kspdg_envs.lbg1") + +SOLVE_LBG1_JL_PATH = get_private_src_module_str("kspdg_envs.lbg1") SOLVE_LBG1_JL_PATH = SOLVE_LBG1_JL_PATH.replace('.','/') SOLVE_LBG1_JL_PATH = "../../src/"+SOLVE_LBG1_JL_PATH+"/solve_lbg1.jl" SOLVE_LBG1_JL_PATH = Path(THIS_FILE.parent, SOLVE_LBG1_JL_PATH)