# Path: tests/fixture_data.py
# PYTHON_SOURCE = """
# import os
# 
# print("I'm just a poor boy without any utils.")
# """
# 
# PYTHON_SOURCE_DEFINING_PIPELINE = """
# from valohai import Pipeline
# 
# 
# def main(config) -> Pipeline:
#     pipe = Pipeline(name="mypipeline", config=config)
# 
#     # Define nodes
#     extract = pipe.execution("Batch feature extraction")
#     train = pipe.task("Train model")
#     evaluate = pipe.execution("Evaluate")
# 
#     # Configure training task node
#     train.linear_parameter("learning_rate", min=0, max=1, step=0.1)
# 
#     # Configure pipeline
#     extract.output("a*").to(train.input("aaa"))
#     extract.output("a*").to(train.input("bbb"))
#     train.output("*").to(evaluate.input("models"))
# 
#     return pipe
# """
# 
# YAML_WITH_EXTRACT_TRAIN_EVAL = """
# - step:
#     name: Batch feature extraction
#     image: ubuntu:18.04
#     command:
#       - date > /valohai/outputs/aaa.md5
#       - date > /valohai/outputs/bbb.sha256
# - step:
#     name: Train model
#     image: ubuntu:18.04
#     command:
#       - find /valohai/inputs -type f -exec sha1sum '{}' ';' > /valohai/outputs/model.txt
#     parameters:
#       - name: learning_rate
#         pass-as: --learning_rate={v}
#         description: Initial learning rate
#         type: float
#         default: 0.001
#     inputs:
#       - name: aaa
#       - name: bbb
# - step:
#     name: Evaluate
#     image: ubuntu:18.04
#     inputs:
#       - name: models
#     command:
#       - ls -lar
# """
# 
# YAML_WITH_TRAIN_EVAL = """
# - step:
#     name: Train model
#     image: ubuntu:18.04
#     command:
#       - find /valohai/inputs -type f -exec sha1sum '{}' ';' > /valohai/outputs/model.txt
#     parameters:
#       - name: learning_rate
#         pass-as: --learning_rate={v}
#         description: Initial learning rate
#         type: float
#         default: 0.001
#     inputs:
#       - name: aaa
#       - name: bbb
# - step:
#     name: Evaluate
#     image: ubuntu:18.04
#     inputs:
#       - name: models
#     command:
#       - ls -lar
# """
# 
# Path: valohai_cli/commands/yaml/pipeline.py
# @click.command()
# @click.argument(
#     "filenames",
#     nargs=-1,
#     type=click.Path(file_okay=True, exists=True, dir_okay=False),
#     required=True,
# )
# def pipeline(filenames: List[str]) -> None:
#     """
#     Update a pipeline config(s) in valohai.yaml based on Python source file(s).
#     Python source file is expected to have def main(config: Config) -> Pipeline
# 
#     Example:
# 
#         vh yaml pipeline mypipeline.py
# 
#     :param filenames: Path(s) of the Python source code files.
#     """
#     project = get_project(require=True)
#     yaml_filename = project.get_config_filename()
#     did_update = False
#     for source_path in filenames:
#         old_config = get_current_config(project)
#         try:
#             new_config = get_pipeline_from_source(source_path, old_config)
#         except Exception:
#             error(
#                 f"Retrieving a new pipeline definition for project {project} for {source_path} failed.\n"
#                 f"The configuration file in use is {yaml_filename}. "
#                 f"See the full traceback below."
#             )
#             raise
# 
#         merged_config = old_config.merge_with(new_config)
#         if old_config.serialize() != merged_config.serialize():
#             with open(yaml_filename, "w") as out_file:
#                 out_file.write(config_to_yaml(merged_config))
#             did_update = True
#     if did_update:
#         info(f"{yaml_filename} updated.")
#     else:
#         info(f"{yaml_filename} already up-to-date.")
# 
# Path: valohai_cli/ctx.py
# @overload
# def get_project(dir: Optional[str] = None, require: Literal[True] = True) -> ProjectOrRemoteProject:
#     ...


import os
from tests.fixture_data import (
    PYTHON_SOURCE,
    PYTHON_SOURCE_DEFINING_PIPELINE,
    YAML_WITH_EXTRACT_TRAIN_EVAL,
    YAML_WITH_TRAIN_EVAL,
)
from valohai_cli.commands.yaml.pipeline import pipeline
from valohai_cli.ctx import get_project

    # Try to generate pipeline from random .py source code.
    source_path = os.path.join(get_project().directory, 'random.py')
    with open(source_path, 'w') as python_fp:
        python_fp.write(PYTHON_SOURCE)
    args = ([source_path])
    rv = runner.invoke(pipeline, args, catch_exceptions=True)
    # The .py source was random so we should get an error
    assert isinstance(rv.exception, AttributeError)

    # Try generating pipeline from .py source code that is correctly using valohai-utils & papi
    source_path = os.path.join(get_project().directory, 'mysnake.py')
    with open(source_path, 'w') as python_fp:
        python_fp.write(PYTHON_SOURCE_DEFINING_PIPELINE)
    args = ([source_path])
    rv = runner.invoke(pipeline, args, catch_exceptions=True)
    # The current config is missing one of the three steps on purpose. This should raise an exception.
    assert isinstance(rv.exception, ValueError)

    # Now re-create config with all the three steps
    config_path = os.path.join(get_project().directory, 'valohai.yaml')
    with open(config_path, 'w') as yaml_fp:
        yaml_fp.write(YAML_WITH_EXTRACT_TRAIN_EVAL)

    # With all the three steps, we are expecting great success
    args = ([source_path])
    rv = runner.invoke(pipeline, args, catch_exceptions=False)
    assert "valohai.yaml updated." in rv.output

    # Try the same update again

# Path: tests/fixture_data.py
# PYTHON_SOURCE = """
# import os
# 
# print("I'm just a poor boy without any utils.")
# """
# 
# PYTHON_SOURCE_DEFINING_PIPELINE = """
# from valohai import Pipeline
# 
# 
# def main(config) -> Pipeline:
#     pipe = Pipeline(name="mypipeline", config=config)
# 
#     # Define nodes
#     extract = pipe.execution("Batch feature extraction")
#     train = pipe.task("Train model")
#     evaluate = pipe.execution("Evaluate")
# 
#     # Configure training task node
#     train.linear_parameter("learning_rate", min=0, max=1, step=0.1)
# 
#     # Configure pipeline
#     extract.output("a*").to(train.input("aaa"))
#     extract.output("a*").to(train.input("bbb"))
#     train.output("*").to(evaluate.input("models"))
# 
#     return pipe
# """
# 
# YAML_WITH_EXTRACT_TRAIN_EVAL = """
# - step:
#     name: Batch feature extraction
#     image: ubuntu:18.04
#     command:
#       - date > /valohai/outputs/aaa.md5
#       - date > /valohai/outputs/bbb.sha256
# - step:
#     name: Train model
#     image: ubuntu:18.04
#     command:
#       - find /valohai/inputs -type f -exec sha1sum '{}' ';' > /valohai/outputs/model.txt
#     parameters:
#       - name: learning_rate
#         pass-as: --learning_rate={v}
#         description: Initial learning rate
#         type: float
#         default: 0.001
#     inputs:
#       - name: aaa
#       - name: bbb
# - step:
#     name: Evaluate
#     image: ubuntu:18.04
#     inputs:
#       - name: models
#     command:
#       - ls -lar
# """
# 
# YAML_WITH_TRAIN_EVAL = """
# - step:
#     name: Train model
#     image: ubuntu:18.04
#     command:
#       - find /valohai/inputs -type f -exec sha1sum '{}' ';' > /valohai/outputs/model.txt
#     parameters:
#       - name: learning_rate
#         pass-as: --learning_rate={v}
#         description: Initial learning rate
#         type: float
#         default: 0.001
#     inputs:
#       - name: aaa
#       - name: bbb
# - step:
#     name: Evaluate
#     image: ubuntu:18.04
#     inputs:
#       - name: models
#     command:
#       - ls -lar
# """
# 
# Path: valohai_cli/commands/yaml/pipeline.py
# @click.command()
# @click.argument(
#     "filenames",
#     nargs=-1,
#     type=click.Path(file_okay=True, exists=True, dir_okay=False),
#     required=True,
# )
# def pipeline(filenames: List[str]) -> None:
#     """
#     Update a pipeline config(s) in valohai.yaml based on Python source file(s).
#     Python source file is expected to have def main(config: Config) -> Pipeline
# 
#     Example:
# 
#         vh yaml pipeline mypipeline.py
# 
#     :param filenames: Path(s) of the Python source code files.
#     """
#     project = get_project(require=True)
#     yaml_filename = project.get_config_filename()
#     did_update = False
#     for source_path in filenames:
#         old_config = get_current_config(project)
#         try:
#             new_config = get_pipeline_from_source(source_path, old_config)
#         except Exception:
#             error(
#                 f"Retrieving a new pipeline definition for project {project} for {source_path} failed.\n"
#                 f"The configuration file in use is {yaml_filename}. "
#                 f"See the full traceback below."
#             )
#             raise
# 
#         merged_config = old_config.merge_with(new_config)
#         if old_config.serialize() != merged_config.serialize():
#             with open(yaml_filename, "w") as out_file:
#                 out_file.write(config_to_yaml(merged_config))
#             did_update = True
#     if did_update:
#         info(f"{yaml_filename} updated.")
#     else:
#         info(f"{yaml_filename} already up-to-date.")
# 
# Path: valohai_cli/ctx.py
# @overload
# def get_project(dir: Optional[str] = None, require: Literal[True] = True) -> ProjectOrRemoteProject:
#     ...


# Path: tests/commands/yaml/test_pipeline.py
import os
from tests.fixture_data import (
    PYTHON_SOURCE,
    PYTHON_SOURCE_DEFINING_PIPELINE,
    YAML_WITH_EXTRACT_TRAIN_EVAL,
    YAML_WITH_TRAIN_EVAL,
)
from valohai_cli.commands.yaml.pipeline import pipeline
from valohai_cli.ctx import get_project


    # Try to generate pipeline from random .py source code.
    source_path = os.path.join(get_project().directory, 'random.py')
    with open(source_path, 'w') as python_fp:
        python_fp.write(PYTHON_SOURCE)
    args = ([source_path])
    rv = runner.invoke(pipeline, args, catch_exceptions=True)
    # The .py source was random so we should get an error
    assert isinstance(rv.exception, AttributeError)

    # Try generating pipeline from .py source code that is correctly using valohai-utils & papi
    source_path = os.path.join(get_project().directory, 'mysnake.py')
    with open(source_path, 'w') as python_fp:
        python_fp.write(PYTHON_SOURCE_DEFINING_PIPELINE)
    args = ([source_path])
    rv = runner.invoke(pipeline, args, catch_exceptions=True)
    # The current config is missing one of the three steps on purpose. This should raise an exception.
    assert isinstance(rv.exception, ValueError)

    # Now re-create config with all the three steps
    config_path = os.path.join(get_project().directory, 'valohai.yaml')
    with open(config_path, 'w') as yaml_fp:
        yaml_fp.write(YAML_WITH_EXTRACT_TRAIN_EVAL)

    # With all the three steps, we are expecting great success
    args = ([source_path])
    rv = runner.invoke(pipeline, args, catch_exceptions=False)
    assert "valohai.yaml updated." in rv.output

    # Try the same update again

    rv = runner.invoke(pipeline, args, catch_exceptions=False)