Example #1
0
def test_diff_for_humans_accuracy():
    now = pendulum.now("utc")

    with pendulum.test(now.add(microseconds=200)):
        assert "1 year" == now.add(years=1).diff_for_humans(absolute=True)
        assert "11 months" == now.add(months=11).diff_for_humans(absolute=True)
        assert "4 weeks" == now.add(days=27).diff_for_humans(absolute=True)
        assert "1 year" == now.add(years=1, months=3).diff_for_humans(absolute=True)
        assert "2 years" == now.add(years=1, months=8).diff_for_humans(absolute=True)

    # DST
    now = pendulum.datetime(2017, 3, 7, tz="America/Toronto")
    with pendulum.test(now):
        assert "6 days" == now.add(days=6).diff_for_humans(absolute=True)
Example #2
0
def test_diff_for_humans_absolute_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(hours=3), True) == "3 hours"
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(hours=3), True) == "3 hours"
Example #3
0
def test_diff_for_humans_other_and_month():
    with pendulum.test(pendulum.datetime(2012, 1, 1, 1, 2, 3)):
        assert "4 weeks before" == pendulum.now().diff_for_humans(
            pendulum.now().add(weeks=4)
        )
        assert "1 month before" == pendulum.now().diff_for_humans(
            pendulum.now().add(months=1)
        )
Example #4
0
def test_diff_for_humans_other_and_future_month():
    with pendulum.test(pendulum.datetime(2012, 1, 1, 1, 2, 3)):
        assert "4 weeks after" == pendulum.now().diff_for_humans(
            pendulum.now().subtract(weeks=4)
        )
        assert "1 month after" == pendulum.now().diff_for_humans(
            pendulum.now().subtract(months=1)
        )
Example #5
0
def test_diff_for_humans_absolute_minutes():
    with pendulum.test(pendulum.datetime(2012, 1, 1, 1, 2, 3)):
        assert "30 minutes" == pendulum.now().diff_for_humans(
            pendulum.now().subtract(minutes=30), True
        )
        assert "30 minutes" == pendulum.now().diff_for_humans(
            pendulum.now().add(minutes=30), True
        )
Example #6
0
def test_diff_for_humans_absolute_seconds():
    with pendulum.test(pendulum.datetime(2012, 1, 1, 1, 2, 3)):
        assert "59 seconds" == pendulum.now().diff_for_humans(
            pendulum.now().subtract(seconds=59), True
        )
        assert "59 seconds" == pendulum.now().diff_for_humans(
            pendulum.now().add(seconds=59), True
        )
Example #7
0
def test_diff_for_humans_absolute_hours():
    with pendulum.test(pendulum.datetime(2012, 1, 1, 1, 2, 3)):
        assert "3 hours" == pendulum.now().diff_for_humans(
            pendulum.now().subtract(hours=3), True
        )
        assert "3 hours" == pendulum.now().diff_for_humans(
            pendulum.now().add(hours=3), True
        )
Example #8
0
def test_diff_for_humans_absolute_months():
    with pendulum.test(pendulum.datetime(2012, 1, 1, 1, 2, 3)):
        assert "2 months" == pendulum.now().diff_for_humans(
            pendulum.now().subtract(months=2), True
        )
        assert "2 months" == pendulum.now().diff_for_humans(
            pendulum.now().add(months=2), True
        )
Example #9
0
def test_diff_for_humans_absolute_years():
    with pendulum.test(pendulum.datetime(2012, 1, 1, 1, 2, 3)):
        assert "1 year" == pendulum.now().diff_for_humans(
            pendulum.now().subtract(years=1), True
        )
        assert "1 year" == pendulum.now().diff_for_humans(
            pendulum.now().add(years=1), True
        )
Example #10
0
def test_diff_for_humans_absolute_minutes():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(minutes=30), True) == "30 minutes"
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(minutes=30), True) == "30 minutes"
Example #11
0
def test_diff_for_humans_absolute_seconds():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(seconds=59), True) == "59 seconds"
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(seconds=59), True) == "59 seconds"
Example #12
0
def test_from_format(text, fmt, expected, now):
    if now is None:
        now = pendulum.datetime(2015, 11, 12)
    else:
        now = pendulum.parse(now)

    # Python 2.7 loses precision for x timestamps
    # so we don't test
    if fmt == "x" and PY2:
        return

    with pendulum.test(now):
        assert pendulum.from_format(text, fmt).isoformat() == expected
Example #13
0
def test_is_birthday():
    with pendulum.test(pendulum.now()):
        d = pendulum.now()
        a_birthday = d.subtract(years=1)
        assert a_birthday.is_birthday()
        not_a_birthday = d.subtract(days=1)
        assert not not_a_birthday.is_birthday()
        also_not_a_birthday = d.add(days=2)
        assert not also_not_a_birthday.is_birthday()

    d1 = pendulum.datetime(1987, 4, 23)
    d2 = pendulum.datetime(2014, 9, 26)
    d3 = pendulum.datetime(2014, 4, 23)
    assert not d2.is_birthday(d1)
    assert d3.is_birthday(d1)
Example #14
0
def test_diff_for_humans_other_and_future_minute():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(minutes=1)) == "1 minute after"
Example #15
0
def test_diff_for_humans_now_and_minute():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(minutes=1).diff_for_humans() == "1 minute ago"
Example #16
0
def test_from_format_error(text, fmt, locale):
    now = pendulum.datetime(2018, 2, 2)

    with pendulum.test(now):
        with pytest.raises(ValueError):
            pendulum.from_format(text, fmt, locale=locale)
def test_failure_recovery_before_run_created(external_repo_context,
                                             crash_location, crash_signal,
                                             capfd):
    # Verify that if the scheduler crashes or is interrupted before a run is created,
    # it will create exactly one tick/run when it is re-launched
    with instance_with_schedules(external_repo_context) as (
            instance,
            _grpc_server_registry,
            external_repo,
    ):
        initial_datetime = to_timezone(
            create_pendulum_time(year=2019,
                                 month=2,
                                 day=27,
                                 hour=0,
                                 minute=0,
                                 second=0,
                                 tz="UTC"),
            "US/Central",
        )

        frozen_datetime = initial_datetime.add()

        external_schedule = external_repo.get_external_schedule(
            "simple_schedule")
        with pendulum.test(frozen_datetime):
            instance.start_schedule_and_update_storage_state(external_schedule)

            debug_crash_flags = {
                external_schedule.name: {
                    crash_location: crash_signal
                }
            }

            scheduler_process = multiprocessing.Process(
                target=_test_launch_scheduled_runs_in_subprocess,
                args=[instance.get_ref(), frozen_datetime, debug_crash_flags],
            )
            scheduler_process.start()
            scheduler_process.join(timeout=60)

            assert scheduler_process.exitcode != 0

            captured = capfd.readouterr()
            assert (
                captured.out.replace("\r\n", "\n") ==
                """2019-02-26 18:00:00 - SchedulerDaemon - INFO - Checking for new runs for the following schedules: simple_schedule
2019-02-26 18:00:00 - SchedulerDaemon - INFO - Evaluating schedule `simple_schedule` at 2019-02-27 00:00:00+0000
""")

            ticks = instance.get_job_ticks(
                external_schedule.get_external_origin_id())
            assert len(ticks) == 1
            assert ticks[0].status == JobTickStatus.STARTED

            assert instance.get_runs_count() == 0

        frozen_datetime = frozen_datetime.add(minutes=5)
        with pendulum.test(frozen_datetime):
            scheduler_process = multiprocessing.Process(
                target=_test_launch_scheduled_runs_in_subprocess,
                args=[instance.get_ref(), frozen_datetime, None],
            )
            scheduler_process.start()
            scheduler_process.join(timeout=60)
            assert scheduler_process.exitcode == 0

            assert instance.get_runs_count() == 1
            wait_for_all_runs_to_start(instance)
            validate_run_started(
                instance.get_runs()[0],
                execution_time=initial_datetime,
                partition_time=create_pendulum_time(2019, 2, 26),
            )

            ticks = instance.get_job_ticks(
                external_schedule.get_external_origin_id())
            assert len(ticks) == 1
            validate_tick(
                ticks[0],
                external_schedule,
                initial_datetime,
                JobTickStatus.SUCCESS,
                [instance.get_runs()[0].run_id],
            )
            captured = capfd.readouterr()
            assert (
                captured.out.replace("\r\n", "\n") ==
                """2019-02-26 18:05:00 - SchedulerDaemon - INFO - Checking for new runs for the following schedules: simple_schedule
2019-02-26 18:05:00 - SchedulerDaemon - INFO - Evaluating schedule `simple_schedule` at 2019-02-27 00:00:00+0000
2019-02-26 18:05:00 - SchedulerDaemon - INFO - Resuming previously interrupted schedule execution
2019-02-26 18:05:00 - SchedulerDaemon - INFO - Completed scheduled launch of run {run_id} for simple_schedule
""".format(run_id=instance.get_runs()[0].run_id))
def test_failure_recovery_between_multi_runs(external_repo_context,
                                             crash_location, crash_signal):
    with instance_with_schedules(external_repo_context) as (
            instance,
            _grpc_server_registry,
            external_repo,
    ):
        initial_datetime = create_pendulum_time(year=2019,
                                                month=2,
                                                day=28,
                                                hour=0,
                                                minute=0,
                                                second=0)
        frozen_datetime = initial_datetime.add()
        external_schedule = external_repo.get_external_schedule(
            "multi_run_schedule")
        with pendulum.test(frozen_datetime):
            instance.start_schedule_and_update_storage_state(external_schedule)

            debug_crash_flags = {
                external_schedule.name: {
                    crash_location: crash_signal
                }
            }

            scheduler_process = multiprocessing.Process(
                target=_test_launch_scheduled_runs_in_subprocess,
                args=[instance.get_ref(), frozen_datetime, debug_crash_flags],
            )
            scheduler_process.start()
            scheduler_process.join(timeout=60)

            assert scheduler_process.exitcode != 0

            wait_for_all_runs_to_start(instance)
            assert instance.get_runs_count() == 1
            validate_run_started(instance.get_runs()[0], initial_datetime)

            ticks = instance.get_job_ticks(
                external_schedule.get_external_origin_id())
            assert len(ticks) == 1

        frozen_datetime = frozen_datetime.add(minutes=1)
        with pendulum.test(frozen_datetime):
            scheduler_process = multiprocessing.Process(
                target=_test_launch_scheduled_runs_in_subprocess,
                args=[instance.get_ref(), frozen_datetime, None],
            )
            scheduler_process.start()
            scheduler_process.join(timeout=60)
            assert scheduler_process.exitcode == 0
            assert instance.get_runs_count() == 2
            validate_run_started(instance.get_runs()[0], initial_datetime)
            ticks = instance.get_job_ticks(
                external_schedule.get_external_origin_id())
            assert len(ticks) == 1
            validate_tick(
                ticks[0],
                external_schedule,
                initial_datetime,
                JobTickStatus.SUCCESS,
                [run.run_id for run in instance.get_runs()],
            )
Example #19
0
def test_simple_sensor(external_repo_context, capfd):
    freeze_datetime = to_timezone(
        create_pendulum_time(year=2019, month=2, day=27, hour=23, minute=59, second=59, tz="UTC"),
        "US/Central",
    )
    with instance_with_sensors(external_repo_context) as (
        instance,
        workspace,
        external_repo,
    ):
        with pendulum.test(freeze_datetime):
            external_sensor = external_repo.get_external_sensor("simple_sensor")
            instance.add_job_state(
                JobState(external_sensor.get_external_origin(), JobType.SENSOR, JobStatus.RUNNING)
            )
            assert instance.get_runs_count() == 0
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 0

            evaluate_sensors(instance, workspace)

            assert instance.get_runs_count() == 0
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 1
            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.SKIPPED,
            )

            captured = capfd.readouterr()
            assert (
                captured.out
                == """2019-02-27 17:59:59 - SensorDaemon - INFO - Checking for new runs for sensor: simple_sensor
2019-02-27 17:59:59 - SensorDaemon - INFO - Sensor returned false for simple_sensor, skipping
"""
            )

            freeze_datetime = freeze_datetime.add(seconds=30)

        with pendulum.test(freeze_datetime):
            evaluate_sensors(instance, workspace)
            wait_for_all_runs_to_start(instance)
            assert instance.get_runs_count() == 1
            run = instance.get_runs()[0]
            validate_run_started(run)
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 2

            expected_datetime = create_pendulum_time(
                year=2019, month=2, day=28, hour=0, minute=0, second=29
            )
            validate_tick(
                ticks[0],
                external_sensor,
                expected_datetime,
                JobTickStatus.SUCCESS,
                [run.run_id],
            )

            captured = capfd.readouterr()
            assert (
                captured.out
                == """2019-02-27 18:00:29 - SensorDaemon - INFO - Checking for new runs for sensor: simple_sensor
2019-02-27 18:00:29 - SensorDaemon - INFO - Launching run for simple_sensor
2019-02-27 18:00:29 - SensorDaemon - INFO - Completed launch of run {run_id} for simple_sensor
""".format(
                    run_id=run.run_id
                )
            )
Example #20
0
def test_diff_for_humans_now_and_future_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.add(hours=2).diff_for_humans() == "in 2 hours"
Example #21
0
def test_diff_for_humans_other_and_seconds():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(
            now.add(seconds=2)) == "a few seconds before"
Example #22
0
def test_diff_for_humans_now_and_future_minute():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.add(minutes=1).diff_for_humans() == "in 1 minute"
Example #23
0
def test_diff_for_humans_now_and_nearly_future_hour():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.add(minutes=59).diff_for_humans() == "in 59 minutes"
Example #24
0
def test_diff_for_humans_now_and_future_seconds():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.add(seconds=2).diff_for_humans() == "in a few seconds"
Example #25
0
def test_diff_for_humans_now_and_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(hours=2).diff_for_humans() == "2 hours ago"
Example #26
0
def test_diff_for_humans_now_and_nearly_hour():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(minutes=59).diff_for_humans() == "59 minutes ago"
Example #27
0
def test_diff_for_humans_other_and_future_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(hours=2)) == "2 hours after"
Example #28
0
def test_diff_for_humans_now_and_minute():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(minutes=1).diff_for_humans() == "1 minute ago"
Example #29
0
def test_launch_once(external_repo_context, capfd):
    freeze_datetime = to_timezone(
        create_pendulum_time(
            year=2019,
            month=2,
            day=27,
            hour=23,
            minute=59,
            second=59,
            tz="UTC",
        ),
        "US/Central",
    )
    with instance_with_sensors(external_repo_context) as (
        instance,
        workspace,
        external_repo,
    ):
        with pendulum.test(freeze_datetime):

            external_sensor = external_repo.get_external_sensor("run_key_sensor")
            instance.add_job_state(
                JobState(external_sensor.get_external_origin(), JobType.SENSOR, JobStatus.RUNNING)
            )
            assert instance.get_runs_count() == 0
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 0

            evaluate_sensors(instance, workspace)
            wait_for_all_runs_to_start(instance)

            assert instance.get_runs_count() == 1
            run = instance.get_runs()[0]
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 1
            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.SUCCESS,
                expected_run_ids=[run.run_id],
            )

        # run again (after 30 seconds), to ensure that the run key maintains idempotence
        freeze_datetime = freeze_datetime.add(seconds=30)
        with pendulum.test(freeze_datetime):
            evaluate_sensors(instance, workspace)
            assert instance.get_runs_count() == 1
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 2
            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.SKIPPED,
            )
            captured = capfd.readouterr()
            assert (
                'Skipping 1 run for sensor run_key_sensor already completed with run keys: ["only_once"]'
                in captured.out
            )

            launched_run = instance.get_runs()[0]

            # Manually create a new run with the same tags
            execute_pipeline(
                the_pipeline,
                run_config=launched_run.run_config,
                tags=launched_run.tags,
                instance=instance,
            )

            # Sensor loop still executes
        freeze_datetime = freeze_datetime.add(seconds=30)
        with pendulum.test(freeze_datetime):
            evaluate_sensors(instance, workspace)
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())

            assert len(ticks) == 3
            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.SKIPPED,
            )
Example #30
0
def test_diff_for_humans_now_and_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(hours=2).diff_for_humans() == "2 hours ago"
Example #31
0
def test_wrong_config_sensor(external_repo_context, capfd):
    freeze_datetime = to_timezone(
        create_pendulum_time(
            year=2019,
            month=2,
            day=27,
            hour=23,
            minute=59,
            second=59,
        ),
        "US/Central",
    )
    with instance_with_sensors(external_repo_context) as (
        instance,
        workspace,
        external_repo,
    ):
        with pendulum.test(freeze_datetime):
            external_sensor = external_repo.get_external_sensor("wrong_config_sensor")
            instance.add_job_state(
                JobState(external_sensor.get_external_origin(), JobType.SENSOR, JobStatus.RUNNING)
            )
            assert instance.get_runs_count() == 0
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 0

            evaluate_sensors(instance, workspace)
            assert instance.get_runs_count() == 0
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 1

            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.FAILURE,
                [],
                "Error in config for pipeline the_pipeline",
            )

            captured = capfd.readouterr()
            assert ("Error in config for pipeline the_pipeline") in captured.out

            # Error repeats on subsequent ticks

            evaluate_sensors(instance, workspace)
            assert instance.get_runs_count() == 0
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 2

            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.FAILURE,
                [],
                "Error in config for pipeline the_pipeline",
            )

            captured = capfd.readouterr()
            assert ("Error in config for pipeline the_pipeline") in captured.out
Example #32
0
def test_diff_for_humans_now_and_future_minute():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.add(minutes=1).diff_for_humans() == "in 1 minute"
Example #33
0
def test_from_format_with_locale(text, fmt, expected):
    now = pendulum.datetime(2018, 2, 2)

    with pendulum.test(now):
        formatted = pendulum.from_format(text, fmt, locale='fr').isoformat()
        assert formatted == expected
Example #34
0
def test_diff_for_humans_now_and_future_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.add(hours=2).diff_for_humans() == "in 2 hours"
Example #35
0
def test_partitions_for_hourly_schedule_decorators_with_timezone(partition_hours_offset: int):
    with pendulum.test(create_pendulum_time(2019, 2, 27, 0, 1, 1, tz="US/Central")):
        start_date = datetime(year=2019, month=1, day=1)

        # You can specify a start date with no timezone and it will be assumed to be
        # in the execution timezone

        @hourly_schedule(
            pipeline_name="foo_pipeline",
            start_date=start_date,
            execution_time=time(hour=0, minute=25),
            execution_timezone="US/Central",
            partition_hours_offset=partition_hours_offset,
        )
        def hourly_central_schedule(hourly_time):
            return {"hourly_time": hourly_time.isoformat()}

        assert hourly_central_schedule.execution_timezone == "US/Central"

        _check_partitions(
            hourly_central_schedule,
            HOURS_UNTIL_FEBRUARY_27 + 1 - partition_hours_offset,
            pendulum.instance(start_date, tz="US/Central"),
            DEFAULT_HOURLY_FORMAT_WITH_TIMEZONE,
            relativedelta(hours=1),
        )

        valid_time = create_pendulum_time(
            year=2019, month=1, day=27, hour=1, minute=25, tz="US/Central"
        )
        context_with_valid_time = build_schedule_context(scheduled_execution_time=valid_time)

        execution_data = hourly_central_schedule.evaluate_tick(context_with_valid_time)
        assert execution_data.run_requests
        assert len(execution_data.run_requests) == 1
        assert execution_data.run_requests[0].run_config == {
            "hourly_time": create_pendulum_time(year=2019, month=1, day=27, hour=1, tz="US/Central")
            .subtract(hours=partition_hours_offset)
            .isoformat()
        }

        # You can specify a start date in a different timezone and it will be transformed into the
        # execution timezone
        start_date_with_different_timezone = create_pendulum_time(2019, 1, 1, 0, tz="US/Pacific")

        @hourly_schedule(
            pipeline_name="foo_pipeline",
            start_date=start_date_with_different_timezone,
            execution_time=time(hour=0, minute=25),
            execution_timezone="US/Central",
            partition_hours_offset=partition_hours_offset,
        )
        def hourly_central_schedule_with_timezone_start_time(hourly_time):
            return {"hourly_time": hourly_time.isoformat()}

        _check_partitions(
            hourly_central_schedule_with_timezone_start_time,
            HOURS_UNTIL_FEBRUARY_27
            - 2  # start date is two hours later since it's in PT
            + 1
            - partition_hours_offset,
            to_timezone(start_date_with_different_timezone, "US/Central"),
            DEFAULT_HOURLY_FORMAT_WITH_TIMEZONE,
            relativedelta(hours=1),
        )
Example #36
0
def test_diff_for_humans_other_and_minute():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(minutes=1)) == "1 minute before"
def test_failure_recovery_after_run_created(external_repo_context,
                                            crash_location, crash_signal,
                                            capfd):
    # Verify that if the scheduler crashes or is interrupted after a run is created,
    # it will just re-launch the already-created run when it runs again
    with instance_with_schedules(external_repo_context) as (
            instance,
            _grpc_server_registry,
            external_repo,
    ):
        initial_datetime = create_pendulum_time(year=2019,
                                                month=2,
                                                day=27,
                                                hour=0,
                                                minute=0,
                                                second=0)
        frozen_datetime = initial_datetime.add()
        external_schedule = external_repo.get_external_schedule(
            "simple_schedule")
        with pendulum.test(frozen_datetime):
            instance.start_schedule_and_update_storage_state(external_schedule)

            debug_crash_flags = {
                external_schedule.name: {
                    crash_location: crash_signal
                }
            }

            scheduler_process = multiprocessing.Process(
                target=_test_launch_scheduled_runs_in_subprocess,
                args=[instance.get_ref(), frozen_datetime, debug_crash_flags],
            )
            scheduler_process.start()
            scheduler_process.join(timeout=60)

            assert scheduler_process.exitcode != 0

            capfd.readouterr()

            ticks = instance.get_job_ticks(
                external_schedule.get_external_origin_id())
            assert len(ticks) == 1
            assert ticks[0].status == JobTickStatus.STARTED

            assert instance.get_runs_count() == 1

            if crash_location == "RUN_CREATED":
                run = instance.get_runs()[0]
                # Run was created, but hasn't launched yet
                assert run.tags[
                    SCHEDULED_EXECUTION_TIME_TAG] == frozen_datetime.isoformat(
                    )
                assert run.tags[PARTITION_NAME_TAG] == "2019-02-26"
                assert run.status == PipelineRunStatus.NOT_STARTED
            else:
                # The run was created and launched - running again should do nothing other than
                # moving the tick to success state.

                # The fact that we need to add this line indicates that there is still a theoretical
                # possible race condition - if the scheduler fails after launching a run
                # and then runs again between when the run was launched and when its status is changed to STARTED by the executor, we could
                # end up launching the same run twice. Run queueing or some other way to immediately
                # identify that a run was launched would help eliminate this race condition. For now,
                # eliminate the possibility by waiting for the run to start before running the
                # scheduler again.
                wait_for_all_runs_to_start(instance)

                run = instance.get_runs()[0]
                validate_run_started(instance.get_runs()[0], frozen_datetime,
                                     create_pendulum_time(2019, 2, 26))

                assert run.status in [
                    PipelineRunStatus.STARTED, PipelineRunStatus.SUCCESS
                ]

        frozen_datetime = frozen_datetime.add(minutes=5)
        with pendulum.test(frozen_datetime):

            # Running again just launches the existing run and marks the tick as success
            scheduler_process = multiprocessing.Process(
                target=_test_launch_scheduled_runs_in_subprocess,
                args=[instance.get_ref(), frozen_datetime, None],
            )
            scheduler_process.start()
            scheduler_process.join(timeout=60)
            assert scheduler_process.exitcode == 0

            assert instance.get_runs_count() == 1
            wait_for_all_runs_to_start(instance)
            validate_run_started(instance.get_runs()[0], initial_datetime,
                                 create_pendulum_time(2019, 2, 26))

            ticks = instance.get_job_ticks(
                external_schedule.get_external_origin_id())
            assert len(ticks) == 1
            validate_tick(
                ticks[0],
                external_schedule,
                initial_datetime,
                JobTickStatus.SUCCESS,
                [instance.get_runs()[0].run_id],
            )

            captured = capfd.readouterr()
            if crash_location == "RUN_CREATED":
                assert (
                    "Run {run_id} already created for this execution of simple_schedule"
                    .format(run_id=instance.get_runs()[0].run_id)
                    in captured.out)
            else:
                assert (
                    "Run {run_id} already completed for this execution of simple_schedule"
                    .format(run_id=instance.get_runs()[0].run_id)
                    in captured.out)
Example #38
0
def test_diff_for_humans_other_and_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(hours=2)) == "2 hours before"
Example #39
0
def test_diff_for_humans_other_and_minute():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(minutes=1)) == "1 minute before"
Example #40
0
def test_diff_for_humans_now_and_seconds():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(seconds=2).diff_for_humans() == "a few seconds ago"
Example #41
0
def test_diff_for_humans_now_and_nearly_hour():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(minutes=59).diff_for_humans() == "59 minutes ago"
Example #42
0
def test_diff_for_humans_other_and_nearly_hour():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(minutes=59)) == "59 minutes before"
Example #43
0
def test_diff_for_humans_now_and_future_seconds():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.add(seconds=2).diff_for_humans() == "in a few seconds"
Example #44
0
def test_diff_for_humans_other_and_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(hours=2)) == "2 hours before"
Example #45
0
def test_diff_for_humans_now_and_nearly_future_hour():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.add(minutes=59).diff_for_humans() == "in 59 minutes"
Example #46
0
def test_diff_for_humans_other_and_future_second():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(
            now.subtract(seconds=1)) == "a few seconds after"
Example #47
0
def test_diff_for_humans_other_and_seconds():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(seconds=2)) == "a few seconds before"
Example #48
0
def test_diff_for_humans_other_and_future_minute():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(minutes=1)) == "1 minute after"
Example #49
0
def test_diff_for_humans_other_and_nearly_hour():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.add(minutes=59)) == "59 minutes before"
Example #50
0
def test_diff_for_humans_other_and_nearly_future_hour():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(
            now.subtract(minutes=59)) == "59 minutes after"
Example #51
0
def test_diff_for_humans_other_and_future_second():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(seconds=1)) == "a few seconds after"
Example #52
0
def test_diff_for_humans_other_and_future_hours():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(hours=2)) == "2 hours after"
Example #53
0
def test_diff_for_humans_other_and_nearly_future_hour():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans(now.subtract(minutes=59)) == "59 minutes after"
Example #54
0
def test_diff_in_seconds_vs_default_now():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(hours=1).diff().in_seconds() == 3600
Example #55
0
def test_diff_in_seconds_vs_default_now():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.subtract(hours=1).diff().in_seconds() == 3600
Example #56
0
def test_launch_once(external_repo_context, capfd):
    freeze_datetime = pendulum.datetime(
        year=2019,
        month=2,
        day=27,
        hour=23,
        minute=59,
        second=59,
    ).in_tz("US/Central")
    with instance_with_sensors(external_repo_context) as (instance, external_repo):
        with pendulum.test(freeze_datetime):

            external_sensor = external_repo.get_external_sensor("run_key_sensor")
            instance.add_job_state(
                JobState(external_sensor.get_external_origin(), JobType.SENSOR, JobStatus.RUNNING)
            )
            assert instance.get_runs_count() == 0
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 0

            list(execute_sensor_iteration(instance, get_default_daemon_logger("SensorDaemon")))
            wait_for_all_runs_to_start(instance)

            assert instance.get_runs_count() == 1
            run = instance.get_runs()[0]
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 1
            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.SUCCESS,
                expected_run_ids=[run.run_id],
            )

        # run again (after 30 seconds), to ensure that the run key maintains idempotence
        freeze_datetime = freeze_datetime.add(seconds=30)
        with pendulum.test(freeze_datetime):
            list(execute_sensor_iteration(instance, get_default_daemon_logger("SensorDaemon")))
            assert instance.get_runs_count() == 1
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())
            assert len(ticks) == 2
            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.SKIPPED,
            )
            captured = capfd.readouterr()
            assert (
                f"Run {run.run_id} already completed with the run key `only_once` for run_key_sensor"
                in captured.out
            )

            launched_run = instance.get_runs()[0]

            # Manually create a new run with the same tags
            execute_pipeline(
                the_pipeline,
                run_config=launched_run.run_config,
                tags=launched_run.tags,
                instance=instance,
            )

            # Sensor loop still executes
        freeze_datetime = freeze_datetime.add(seconds=30)
        with pendulum.test(freeze_datetime):
            list(execute_sensor_iteration(instance, get_default_daemon_logger("SensorDaemon")))
            ticks = instance.get_job_ticks(external_sensor.get_external_origin_id())

            assert len(ticks) == 3
            validate_tick(
                ticks[0],
                external_sensor,
                freeze_datetime,
                JobTickStatus.SKIPPED,
            )
Example #57
0
def test_diff_for_humans():
    with pendulum.test(pendulum.datetime(2016, 8, 29)):
        diff_for_humans()
def test_failure_recovery_after_tick_success(external_repo_context,
                                             crash_location, crash_signal):
    # Verify that if the scheduler crashes or is interrupted after a run is created,
    # it will just re-launch the already-created run when it runs again
    with instance_with_schedules(external_repo_context) as (
            instance,
            _grpc_server_registry,
            external_repo,
    ):
        initial_datetime = create_pendulum_time(year=2019,
                                                month=2,
                                                day=27,
                                                hour=0,
                                                minute=0,
                                                second=0)
        frozen_datetime = initial_datetime.add()
        external_schedule = external_repo.get_external_schedule(
            "simple_schedule")
        with pendulum.test(frozen_datetime):
            instance.start_schedule_and_update_storage_state(external_schedule)

            debug_crash_flags = {
                external_schedule.name: {
                    crash_location: crash_signal
                }
            }

            scheduler_process = multiprocessing.Process(
                target=_test_launch_scheduled_runs_in_subprocess,
                args=[instance.get_ref(), frozen_datetime, debug_crash_flags],
            )
            scheduler_process.start()
            scheduler_process.join(timeout=60)

            assert scheduler_process.exitcode != 0

            # As above there's a possible race condition here if the scheduler crashes
            # and launches the same run twice if we crash right after the launch and re-run
            # before the run actually starts
            wait_for_all_runs_to_start(instance)

            assert instance.get_runs_count() == 1
            validate_run_started(instance.get_runs()[0], initial_datetime,
                                 create_pendulum_time(2019, 2, 26))

            ticks = instance.get_job_ticks(
                external_schedule.get_external_origin_id())
            assert len(ticks) == 1

            if crash_signal == get_terminate_signal():
                run_ids = []
            else:
                run_ids = [run.run_id for run in instance.get_runs()]

            validate_tick(ticks[0], external_schedule, initial_datetime,
                          JobTickStatus.STARTED, run_ids)

        frozen_datetime = frozen_datetime.add(minutes=1)
        with pendulum.test(frozen_datetime):
            # Running again just marks the tick as success since the run has already started
            scheduler_process = multiprocessing.Process(
                target=_test_launch_scheduled_runs_in_subprocess,
                args=[instance.get_ref(), frozen_datetime, None],
            )
            scheduler_process.start()
            scheduler_process.join(timeout=60)
            assert scheduler_process.exitcode == 0

            assert instance.get_runs_count() == 1
            validate_run_started(instance.get_runs()[0], initial_datetime,
                                 create_pendulum_time(2019, 2, 26))

            ticks = instance.get_job_ticks(
                external_schedule.get_external_origin_id())
            assert len(ticks) == 1
            validate_tick(
                ticks[0],
                external_schedule,
                initial_datetime,
                JobTickStatus.SUCCESS,
                [instance.get_runs()[0].run_id],
            )
Example #59
0
def test_diff_for_humans_now_and_second():
    with pendulum.test(pendulum.today().at(12, 34, 56)):
        now = pendulum.now().time()

        assert now.diff_for_humans() == "a few seconds ago"
Example #60
0
def test_partitions_for_hourly_schedule_decorators_with_timezone():
    with instance_for_test() as instance:
        with pendulum.test(
                pendulum.create(2019, 2, 27, 0, 1, 1, tz="US/Central")):
            start_date = datetime(year=2019, month=1, day=1, minute=1)

            # You can specify a start date with no timezone and it will be assumed to be
            # in the execution timezone

            @hourly_schedule(
                pipeline_name="foo_pipeline",
                start_date=start_date,
                execution_time=time(hour=0, minute=25),
                execution_timezone="US/Central",
            )
            def hourly_central_schedule(hourly_time):
                return {"hourly_time": hourly_time.isoformat()}

            assert hourly_central_schedule.execution_timezone == "US/Central"

            _check_partitions(
                hourly_central_schedule,
                HOURS_UNTIL_FEBRUARY_27,
                pendulum.instance(start_date, tz="US/Central"),
                DEFAULT_HOURLY_FORMAT_WITH_TIMEZONE,
                relativedelta(hours=1),
            )

            valid_time = pendulum.create(year=2019,
                                         month=1,
                                         day=27,
                                         hour=1,
                                         minute=25,
                                         tz="US/Central")
            context_with_valid_time = ScheduleExecutionContext(
                instance, valid_time)

            assert hourly_central_schedule.get_run_config(
                context_with_valid_time) == {
                    "hourly_time":
                    pendulum.create(year=2019,
                                    month=1,
                                    day=27,
                                    hour=0,
                                    minute=1,
                                    tz="US/Central").isoformat()
                }

            assert hourly_central_schedule.should_execute(
                context_with_valid_time)

            # You can specify a start date in a different timezone and it will be transformed into the
            # execution timezone
            start_date_with_different_timezone = pendulum.create(
                2019, 1, 1, 0, 1, tz="US/Pacific")

            @hourly_schedule(
                pipeline_name="foo_pipeline",
                start_date=start_date_with_different_timezone,
                execution_time=time(hour=0, minute=25),
                execution_timezone="US/Central",
            )
            def hourly_central_schedule_with_timezone_start_time(hourly_time):
                return {"hourly_time": hourly_time.isoformat()}

            _check_partitions(
                hourly_central_schedule_with_timezone_start_time,
                HOURS_UNTIL_FEBRUARY_27 -
                2,  # start date is two hours later since it's in PT
                start_date_with_different_timezone.in_tz("US/Central"),
                DEFAULT_HOURLY_FORMAT_WITH_TIMEZONE,
                relativedelta(hours=1),
            )