diff --git a/README.md b/README.md index d7b790b..06ff008 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# Timefold Solver +![Timefold Logo](https://raw.githubusercontent.com/TimefoldAI/timefold-solver/main/docs/src/modules/ROOT/images/shared/timefold-logo.png) -[![PyPI](https://img.shields.io/pypi/v/timefold "PyPI")](https://pypi.org/project/timefold-solver/) +# Timefold Solver for Python + +[![PyPI](https://img.shields.io/pypi/v/timefold-solver "PyPI")](https://pypi.org/project/timefold-solver/) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=timefold_solver_python&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=timefold_solver_python) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=timefold_solver_python&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=timefold_solver_python) @@ -51,7 +53,7 @@ To declare Planning Entities, use the `@planning_entity` decorator along with an ```python from dataclasses import dataclass, field from typing import Annotated -from timefold.solver import planning_entity, PlanningId, PlanningVariable +from timefold.solver.domain import planning_entity, PlanningId, PlanningVariable @planning_entity @dataclass @@ -75,7 +77,8 @@ To declare the Planning Solution, use the `@planning_solution` decorator: ```python from dataclasses import dataclass, field from typing import Annotated -from timefold.solver import planning_solution, ProblemFactCollectionProperty, ValueRangeProvider, PlanningEntityCollectionProperty, PlanningScore +from timefold.solver.domain import (planning_solution, ProblemFactCollectionProperty, ValueRangeProvider, + PlanningEntityCollectionProperty, PlanningScore) from timefold.solver.score import HardSoftScore @planning_solution @@ -101,9 +104,7 @@ You define your constraints by using the ConstraintFactory: ```python from domain import Lesson -from timefold.solver import constraint_provider -from timefold.solver.types import Joiners, HardSoftScore - +from timefold.solver.score import Joiners, HardSoftScore, constraint_provider @constraint_provider def define_constraints(constraint_factory): diff --git a/setup.py b/setup.py index 89cb611..58b01ca 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,18 @@ from shutil import copyfile import sys +include_java_stubs = os.environ.get('INCLUDE_JAVA_STUBS', 'false').lower() == 'true' +""" +If Java type stubs should be generated. +These are for our own type checker; users should never need +to use them (since they should be using the Python classes). + +Not included in the default build since IDE's will see multiple +packages that define a class (the one from `ai.timefold...` +and the one from `timefold.solver...`), which can lead to the +wrong one being imported. +""" + class FetchDependencies(build_py): """ @@ -47,7 +59,10 @@ def run(self): subprocess.run([str((project_root / command).absolute()), 'clean', 'install'], cwd=project_root, check=True) - self.create_stubs(project_root, command) + + if include_java_stubs: + self.create_stubs(project_root, command) + classpath_jars = [] # Add the main artifact classpath_jars.extend(glob.glob(os.path.join(project_root, 'timefold-solver-python-core', 'target', '*.jar'))) @@ -73,6 +88,33 @@ def find_stub_files(stub_root: str): yield os.path.join(root, file) +packages = [ + 'timefold.solver', + 'timefold.solver.config', + 'timefold.solver.domain', + 'timefold.solver.heuristic', + 'timefold.solver.score', + 'timefold.solver.test', + '_jpyinterpreter', + ] + +package_dir = { + 'timefold.solver': 'timefold-solver-python-core/src/main/python', + '_jpyinterpreter': 'jpyinterpreter/src/main/python', +} + +if include_java_stubs: + packages += [ + 'java-stubs', + 'jpype-stubs', + 'ai-stubs', + ] + package_dir.update({ + 'java-stubs': 'timefold-solver-python-core/src/main/resources', + 'jpype-stubs': 'timefold-solver-python-core/src/main/resources', + 'ai-stubs': 'timefold-solver-python-core/src/main/resources', + }) + this_directory = Path(__file__).parent long_description = (this_directory / "README.md").read_text() timefold_solver_python_version = '999-dev0' @@ -98,28 +140,8 @@ def find_stub_files(stub_root: str): 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent' ], - packages=['timefold.solver', - 'timefold.solver.config', - 'timefold.solver.domain', - 'timefold.solver.heuristic', - 'timefold.solver.score', - 'timefold.solver.test', - 'jpyinterpreter', - 'java-stubs', - 'jpype-stubs', - 'ai-stubs'], - package_dir={ - 'timefold.solver': 'timefold-solver-python-core/src/main/python', - 'jpyinterpreter': 'jpyinterpreter/src/main/python', - # Setup tools need a non-empty directory to use as base - # Since these packages are generated during the build, - # we use the src/main/resources package, which does - # not contain any python files and is already included - # in the build - 'java-stubs': 'timefold-solver-python-core/src/main/resources', - 'jpype-stubs': 'timefold-solver-python-core/src/main/resources', - 'ai-stubs': 'timefold-solver-python-core/src/main/resources', - }, + packages=packages, + package_dir=package_dir, test_suite='tests', python_requires='>=3.10', install_requires=[ diff --git a/tests/test_solver_config.py b/tests/test_solver_config.py index d621273..5020b51 100644 --- a/tests/test_solver_config.py +++ b/tests/test_solver_config.py @@ -47,7 +47,7 @@ def get_java_solver_config(path: pathlib.Path): def test_load_from_solver_config_file(): - from jpyinterpreter import get_java_type_for_python_type + from _jpyinterpreter import get_java_type_for_python_type solver_config = get_java_solver_config(pathlib.Path('tests', 'solverConfig-simple.xml')) assert solver_config.getSolutionClass() == get_java_type_for_python_type(Solution).getJavaClass() entity_class_list = solver_config.getEntityClassList() @@ -59,7 +59,7 @@ def test_load_from_solver_config_file(): def test_reload_from_solver_config_file(): - from jpyinterpreter import get_java_type_for_python_type + from _jpyinterpreter import get_java_type_for_python_type @planning_solution class RedefinedSolution: diff --git a/timefold-solver-python-core/src/main/python/_problem_change.py b/timefold-solver-python-core/src/main/python/_problem_change.py index 8c1d94f..f52b5da 100644 --- a/timefold-solver-python-core/src/main/python/_problem_change.py +++ b/timefold-solver-python-core/src/main/python/_problem_change.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from typing import TypeVar, Optional, Callable, TYPE_CHECKING, Generic from types import FunctionType -from jpyinterpreter import (convert_to_java_python_like_object, +from _jpyinterpreter import (convert_to_java_python_like_object, unwrap_python_like_object, update_python_object_from_java, translate_python_bytecode_to_java_bytecode) diff --git a/timefold-solver-python-core/src/main/python/_solution_manager.py b/timefold-solver-python-core/src/main/python/_solution_manager.py index d8e2dd0..c8544c0 100644 --- a/timefold-solver-python-core/src/main/python/_solution_manager.py +++ b/timefold-solver-python-core/src/main/python/_solution_manager.py @@ -59,7 +59,7 @@ def update(self, solution: Solution_, solution_update_policy=None) -> 'Score': The score of the updated solution. """ # TODO handle solution_update_policy - from jpyinterpreter import convert_to_java_python_like_object, update_python_object_from_java + from _jpyinterpreter import convert_to_java_python_like_object, update_python_object_from_java java_solution = convert_to_java_python_like_object(solution) out = self._delegate.update(java_solution) update_python_object_from_java(java_solution) @@ -84,7 +84,7 @@ def analyze(self, solution: Solution_, score_analysis_fetch_policy=None, solutio The `ScoreAnalysis` corresponding to the given solution. """ # TODO handle policies - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object return ScoreAnalysis(self._delegate.analyze(convert_to_java_python_like_object(solution))) def explain(self, solution: Solution_, solution_update_policy=None) -> 'ScoreExplanation': @@ -104,7 +104,7 @@ def explain(self, solution: Solution_, solution_update_policy=None) -> 'ScoreExp The `ScoreExplanation` corresponding to the given solution. """ # TODO handle policies - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object return ScoreExplanation(self._delegate.explain(convert_to_java_python_like_object(solution))) def recommend_fit(self, solution: Solution_, entity_or_element, proposition_function, diff --git a/timefold-solver-python-core/src/main/python/_solver.py b/timefold-solver-python-core/src/main/python/_solver.py index 03fbaa1..4a59764 100644 --- a/timefold-solver-python-core/src/main/python/_solver.py +++ b/timefold-solver-python-core/src/main/python/_solver.py @@ -96,7 +96,7 @@ def solve(self, problem: Solution_): """ from java.lang import Exception as JavaException from ai.timefold.jpyinterpreter.types.errors import PythonBaseException - from jpyinterpreter import convert_to_java_python_like_object, unwrap_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object, unwrap_python_like_object java_problem = convert_to_java_python_like_object(problem) if not self._solution_class.isInstance(java_problem): raise ValueError( @@ -229,7 +229,7 @@ def add_event_listener(self, event_listener: Callable[[BestSolutionChangedEvent[ class EventListener: @JOverride def bestSolutionChanged(self, event): - from jpyinterpreter import unwrap_python_like_object + from _jpyinterpreter import unwrap_python_like_object nonlocal event_listener_list event = BestSolutionChangedEvent( new_best_score=event.getNewBestScore(), diff --git a/timefold-solver-python-core/src/main/python/_solver_manager.py b/timefold-solver-python-core/src/main/python/_solver_manager.py index aa50e6f..66722b5 100644 --- a/timefold-solver-python-core/src/main/python/_solver_manager.py +++ b/timefold-solver-python-core/src/main/python/_solver_manager.py @@ -71,7 +71,7 @@ def get_problem_id(self) -> ProblemId_: ProblemId_ The problem id corresponding to this `SolverJob`. """ - from jpyinterpreter import unwrap_python_like_object + from _jpyinterpreter import unwrap_python_like_object return unwrap_python_like_object(self._delegate.getProblemId()) def get_solver_status(self) -> SolverStatus: @@ -109,7 +109,7 @@ def get_final_best_solution(self) -> Solution_: Solution_ Never ``None``, but it could be the original uninitialized problem. """ - from jpyinterpreter import unwrap_python_like_object + from _jpyinterpreter import unwrap_python_like_object return unwrap_python_like_object(self._delegate.getFinalBestSolution()) def terminate_early(self) -> None: @@ -186,7 +186,7 @@ def with_problem_id(self, problem_id: ProblemId_) -> 'SolverJobBuilder': SolverJobBuilder This `SolverJobBuilder`. """ - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object return SolverJobBuilder(self._delegate.withProblemId(convert_to_java_python_like_object(problem_id))) def with_problem(self, problem: Solution_) -> 'SolverJobBuilder': @@ -203,7 +203,7 @@ def with_problem(self, problem: Solution_) -> 'SolverJobBuilder': SolverJobBuilder This `SolverJobBuilder`. """ - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object return SolverJobBuilder(self._delegate.withProblem(convert_to_java_python_like_object(problem))) def with_config_override(self, config_override: SolverConfigOverride) -> 'SolverJobBuilder': @@ -237,7 +237,7 @@ def with_problem_finder(self, problem_finder: Callable[[ProblemId_], Solution_]) This `SolverJobBuilder`. """ from java.util.function import Function - from jpyinterpreter import convert_to_java_python_like_object, unwrap_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object, unwrap_python_like_object java_finder = Function @ (lambda problem_id: convert_to_java_python_like_object( problem_finder(unwrap_python_like_object(problem_id)))) return SolverJobBuilder(self._delegate.withProblemFinder(java_finder)) @@ -257,7 +257,7 @@ def with_best_solution_consumer(self, best_solution_consumer: Callable[[Solution This `SolverJobBuilder`. """ from java.util.function import Consumer - from jpyinterpreter import unwrap_python_like_object + from _jpyinterpreter import unwrap_python_like_object java_consumer = Consumer @ (lambda solution: best_solution_consumer(unwrap_python_like_object(solution))) return SolverJobBuilder(self._delegate.withBestSolutionConsumer(java_consumer)) @@ -278,7 +278,7 @@ def with_final_best_solution_consumer(self, final_best_solution_consumer: Callab This `SolverJobBuilder`. """ from java.util.function import Consumer - from jpyinterpreter import unwrap_python_like_object + from _jpyinterpreter import unwrap_python_like_object java_consumer = Consumer @ (lambda solution: final_best_solution_consumer(unwrap_python_like_object(solution))) return SolverJobBuilder( @@ -300,7 +300,7 @@ def with_exception_handler(self, exception_handler: Callable[[ProblemId_, Except This `SolverJobBuilder`. """ from java.util.function import BiConsumer - from jpyinterpreter import unwrap_python_like_object + from _jpyinterpreter import unwrap_python_like_object java_consumer = BiConsumer @ (lambda problem_id, error: exception_handler(unwrap_python_like_object(problem_id), error)) @@ -466,7 +466,7 @@ def get_solver_status(self, problem_id: ProblemId_) -> SolverStatus: SolverStatus The `SolverStatus` corresponding to `problem_id`. """ - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object return SolverStatus._from_java_enum(self._delegate.getSolverStatus( convert_to_java_python_like_object(problem_id))) @@ -489,7 +489,7 @@ def terminate_early(self, problem_id: ProblemId_) -> None: A value given to `SolverManager.solve`, `SolverManager.solve_and_listen` or `SolverJobBuilder.with_problem_id`. """ - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object self._delegate.terminateEarly(convert_to_java_python_like_object(problem_id)) def add_problem_change(self, problem_id: ProblemId_, problem_change: ProblemChange[Solution_]) -> Awaitable[None]: @@ -511,7 +511,7 @@ def add_problem_change(self, problem_id: ProblemId_, problem_change: ProblemChan Awaitable An awaitable that completes after the best solution containing this change has been consumed. """ - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object return wrap_future(self._delegate.addProblemChange(convert_to_java_python_like_object(problem_id), ProblemChangeWrapper(problem_change))) diff --git a/timefold-solver-python-core/src/main/python/_timefold_java_interop.py b/timefold-solver-python-core/src/main/python/_timefold_java_interop.py index e01c476..2de3abd 100644 --- a/timefold-solver-python-core/src/main/python/_timefold_java_interop.py +++ b/timefold-solver-python-core/src/main/python/_timefold_java_interop.py @@ -69,7 +69,7 @@ def init(*args, path: list[str] = None, include_timefold_jars: bool = True, log_ log_level : str The logging level to use. """ - from jpyinterpreter import init + from _jpyinterpreter import init if jpype.isJVMStarted(): # noqa raise RuntimeError('JVM already started. Maybe call init before timefold.solver.types imports?') if path is None: @@ -115,7 +115,7 @@ def get_class(python_class: Union[type, Callable]) -> JClass: """Return the Java Class for the given Python Class""" from java.lang import Object, Class from ai.timefold.jpyinterpreter.types.wrappers import OpaquePythonReference - from jpyinterpreter import is_c_native, get_java_type_for_python_type + from _jpyinterpreter import is_c_native, get_java_type_for_python_type if python_class is None: return cast(JClass, None) @@ -146,7 +146,7 @@ def get_asm_type(python_class: Union[type, Callable]) -> Any: from java.lang import Object, Class from ai.timefold.jpyinterpreter import AnnotationMetadata from ai.timefold.jpyinterpreter.types.wrappers import OpaquePythonReference - from jpyinterpreter import is_c_native, get_java_type_for_python_type + from _jpyinterpreter import is_c_native, get_java_type_for_python_type if python_class is None: return None @@ -227,7 +227,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): def compile_class(python_class: type) -> None: - from jpyinterpreter import translate_python_class_to_java_class + from _jpyinterpreter import translate_python_class_to_java_class ensure_init() class_identifier = _get_class_identifier_for_object(python_class) out = translate_python_class_to_java_class(python_class).getJavaClass() diff --git a/timefold-solver-python-core/src/main/python/domain/_annotations.py b/timefold-solver-python-core/src/main/python/domain/_annotations.py index b24fb08..19da539 100644 --- a/timefold-solver-python-core/src/main/python/domain/_annotations.py +++ b/timefold-solver-python-core/src/main/python/domain/_annotations.py @@ -2,7 +2,7 @@ from ._variable_listener import VariableListener from .._timefold_java_interop import ensure_init, get_asm_type -from jpyinterpreter import JavaAnnotation, AnnotationValueSupplier +from _jpyinterpreter import JavaAnnotation, AnnotationValueSupplier from jpype import JImplements, JOverride from typing import Union, List, Callable, Type, TYPE_CHECKING, TypeVar @@ -663,7 +663,7 @@ def planning_entity(entity_class: Type = None, /, *, pinning_filter: Callable = def planning_entity_wrapper(entity_class_argument): from .._timefold_java_interop import _add_to_compilation_queue from ai.timefold.solver.core.api.domain.entity import PinningFilter - from jpyinterpreter import add_class_annotation, translate_python_bytecode_to_java_bytecode + from _jpyinterpreter import add_class_annotation, translate_python_bytecode_to_java_bytecode from typing import get_origin, Annotated planning_pin_field = None @@ -736,7 +736,7 @@ def planning_solution(planning_solution_class: Type[Solution_]) -> Type[Solution ... score: Annotated[HardSoftScore, PlanningScore] """ ensure_init() - from jpyinterpreter import add_class_annotation + from _jpyinterpreter import add_class_annotation from .._timefold_java_interop import _add_to_compilation_queue from ai.timefold.solver.core.api.domain.solution import PlanningSolution as JavaPlanningSolution out = add_class_annotation(JavaPlanningSolution)(planning_solution_class) @@ -764,7 +764,7 @@ def constraint_configuration(constraint_configuration_class: Type[Solution_]) -> ... maximize_value: Annotated[HardSoftScore, ConstraintWeight('Maximize value')] """ ensure_init() - from jpyinterpreter import add_class_annotation + from _jpyinterpreter import add_class_annotation from ai.timefold.solver.core.api.domain.constraintweight import ( ConstraintConfiguration as JavaConstraintConfiguration) out = add_class_annotation(JavaConstraintConfiguration)(constraint_configuration_class) diff --git a/timefold-solver-python-core/src/main/python/domain/_variable_listener.py b/timefold-solver-python-core/src/main/python/domain/_variable_listener.py index 525e331..ee9c031 100644 --- a/timefold-solver-python-core/src/main/python/domain/_variable_listener.py +++ b/timefold-solver-python-core/src/main/python/domain/_variable_listener.py @@ -1,5 +1,5 @@ from ..score import ScoreDirector -from jpyinterpreter import add_java_interface +from _jpyinterpreter import add_java_interface from typing import TYPE_CHECKING, TypeVar if TYPE_CHECKING: diff --git a/timefold-solver-python-core/src/main/python/heuristic/_nearby_selection.py b/timefold-solver-python-core/src/main/python/heuristic/_nearby_selection.py index 6510ef0..a28748b 100644 --- a/timefold-solver-python-core/src/main/python/heuristic/_nearby_selection.py +++ b/timefold-solver-python-core/src/main/python/heuristic/_nearby_selection.py @@ -24,7 +24,7 @@ def nearby_distance_meter(distance_function: Callable[[Origin_, Destination_], f """ ensure_init() - from jpyinterpreter import translate_python_bytecode_to_java_bytecode, generate_proxy_class_for_translated_function + from _jpyinterpreter import translate_python_bytecode_to_java_bytecode, generate_proxy_class_for_translated_function from ai.timefold.solver.core.impl.heuristic.selector.common.nearby import NearbyDistanceMeter # noqa java_class = generate_proxy_class_for_translated_function(NearbyDistanceMeter, translate_python_bytecode_to_java_bytecode( diff --git a/timefold-solver-python-core/src/main/python/score/_annotations.py b/timefold-solver-python-core/src/main/python/score/_annotations.py index 0ef6588..3e20425 100644 --- a/timefold-solver-python-core/src/main/python/score/_annotations.py +++ b/timefold-solver-python-core/src/main/python/score/_annotations.py @@ -81,7 +81,7 @@ def easy_score_calculator(easy_score_calculator_function: Callable[[Solution_], """ ensure_init() - from jpyinterpreter import translate_python_bytecode_to_java_bytecode, generate_proxy_class_for_translated_function + from _jpyinterpreter import translate_python_bytecode_to_java_bytecode, generate_proxy_class_for_translated_function from ai.timefold.solver.core.api.score.calculator import EasyScoreCalculator java_class = generate_proxy_class_for_translated_function(EasyScoreCalculator, translate_python_bytecode_to_java_bytecode( diff --git a/timefold-solver-python-core/src/main/python/score/_function_translator.py b/timefold-solver-python-core/src/main/python/score/_function_translator.py index 56f06e2..623cd5c 100644 --- a/timefold-solver-python-core/src/main/python/score/_function_translator.py +++ b/timefold-solver-python-core/src/main/python/score/_function_translator.py @@ -3,7 +3,7 @@ PythonToIntBiFunction, PythonToIntTriFunction, PythonToIntQuadFunction, PythonPredicate, PythonBiPredicate, PythonTriPredicate, PythonQuadPredicate, PythonPentaPredicate) -from jpyinterpreter import translate_python_bytecode_to_java_bytecode, check_current_python_version_supported +from _jpyinterpreter import translate_python_bytecode_to_java_bytecode, check_current_python_version_supported import jpype.imports # noqa from jpype import JImplements, JOverride import inspect diff --git a/timefold-solver-python-core/src/main/python/score/_incremental_score_calculator.py b/timefold-solver-python-core/src/main/python/score/_incremental_score_calculator.py index d60437e..dee5df1 100644 --- a/timefold-solver-python-core/src/main/python/score/_incremental_score_calculator.py +++ b/timefold-solver-python-core/src/main/python/score/_incremental_score_calculator.py @@ -1,4 +1,4 @@ -from jpyinterpreter import add_java_interface +from _jpyinterpreter import add_java_interface from typing import TYPE_CHECKING from abc import ABC, abstractmethod diff --git a/timefold-solver-python-core/src/main/python/score/_score_analysis.py b/timefold-solver-python-core/src/main/python/score/_score_analysis.py index aee6091..26bb62a 100644 --- a/timefold-solver-python-core/src/main/python/score/_score_analysis.py +++ b/timefold-solver-python-core/src/main/python/score/_score_analysis.py @@ -1,5 +1,5 @@ from .._timefold_java_interop import get_class -from jpyinterpreter import unwrap_python_like_object, add_java_interface +from _jpyinterpreter import unwrap_python_like_object, add_java_interface from dataclasses import dataclass from typing import TypeVar, Generic, Union, TYPE_CHECKING, Any, cast, Optional, Type @@ -359,7 +359,7 @@ def score(self) -> 'Score': @property def solution(self) -> Solution_: - from jpyinterpreter import unwrap_python_like_object + from _jpyinterpreter import unwrap_python_like_object return unwrap_python_like_object(self._delegate.getSolution()) @property diff --git a/timefold-solver-python-core/src/main/python/test/__init__.py b/timefold-solver-python-core/src/main/python/test/__init__.py index 5e46310..cc036b7 100644 --- a/timefold-solver-python-core/src/main/python/test/__init__.py +++ b/timefold-solver-python-core/src/main/python/test/__init__.py @@ -104,7 +104,7 @@ def given(self, *facts) -> 'SingleConstraintAssertion': from ai.timefold.jpyinterpreter.types import CPythonBackedPythonLikeObject # noqa from ai.timefold.jpyinterpreter.types.wrappers import OpaquePythonReference # noqa from java.util import HashMap - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object reference_map = HashMap() wrapped_facts = [] @@ -123,7 +123,7 @@ def given_solution(self, solution: 'Solution_') -> 'SingleConstraintAssertion': solution never ``None`` """ - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object wrapped_solution = convert_to_java_python_like_object(solution) return SingleConstraintAssertion(self.delegate.givenSolution(wrapped_solution)) @@ -144,7 +144,7 @@ def given(self, *facts) -> 'MultiConstraintAssertion': from ai.timefold.jpyinterpreter import CPythonBackedPythonInterpreter # noqa from ai.timefold.jpyinterpreter.types import CPythonBackedPythonLikeObject # noqa from ai.timefold.jpyinterpreter.types.wrappers import OpaquePythonReference # noqa - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object from java.util import HashMap reference_map = HashMap() wrapped_facts = [] @@ -164,7 +164,7 @@ def given_solution(self, solution: 'Solution_') -> 'MultiConstraintAssertion': solution never ``None`` """ - from jpyinterpreter import convert_to_java_python_like_object + from _jpyinterpreter import convert_to_java_python_like_object wrapped_solution = convert_to_java_python_like_object(solution) return MultiConstraintAssertion(self.delegate.givenSolution(wrapped_solution))