diff --git a/py/server/deephaven/experimental/s3.py b/py/server/deephaven/experimental/s3.py index db6168aca16..1fe105e7a40 100644 --- a/py/server/deephaven/experimental/s3.py +++ b/py/server/deephaven/experimental/s3.py @@ -1,16 +1,13 @@ # # Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending # -import datetime from typing import Optional, Union import jpy -import numpy as np -import pandas as pd -from deephaven import time, DHError +from deephaven import DHError from deephaven._wrapper import JObjectWrapper -from deephaven.dtypes import Duration +from deephaven.time import DurationLike, to_j_duration # If we move S3 to a permanent module, we should remove this try/except block and just import the types directly. try: @@ -38,10 +35,8 @@ def __init__(self, max_concurrent_requests: Optional[int] = None, read_ahead_count: Optional[int] = None, fragment_size: Optional[int] = None, - connection_timeout: Union[ - Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta, None] = None, - read_timeout: Union[ - Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta, None] = None, + connection_timeout: Optional[DurationLike] = None, + read_timeout: Optional[DurationLike] = None, access_key_id: Optional[str] = None, secret_access_key: Optional[str] = None, anonymous_access: bool = False, @@ -62,11 +57,11 @@ def __init__(self, fragment. Defaults to 32, which means fetch the next 32 fragments in advance when reading the current fragment. fragment_size (int): the maximum size of each fragment to read, defaults to 64 KiB. If there are fewer bytes remaining in the file, the fetched fragment can be smaller. - connection_timeout (Union[Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta]): + connection_timeout (DurationLike): the amount of time to wait when initially establishing a connection before giving up and timing out, can be expressed as an integer in nanoseconds, a time interval string, e.g. "PT00:00:00.001" or "PT1s", or other time duration types. Default to 2 seconds. - read_timeout (Union[Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta]): + read_timeout (DurationLike): the amount of time to wait when reading a fragment before giving up and timing out, can be expressed as an integer in nanoseconds, a time interval string, e.g. "PT00:00:00.001" or "PT1s", or other time duration types. Default to 2 seconds. @@ -111,10 +106,10 @@ def __init__(self, builder.fragmentSize(fragment_size) if connection_timeout is not None: - builder.connectionTimeout(time.to_j_duration(connection_timeout)) + builder.connectionTimeout(to_j_duration(connection_timeout)) if read_timeout is not None: - builder.readTimeout(time.to_j_duration(read_timeout)) + builder.readTimeout(to_j_duration(read_timeout)) if ((access_key_id is not None and secret_access_key is None) or (access_key_id is None and secret_access_key is not None)): diff --git a/py/server/deephaven/json/__init__.py b/py/server/deephaven/json/__init__.py index 3112b2d68cc..3cc9b986e33 100644 --- a/py/server/deephaven/json/__init__.py +++ b/py/server/deephaven/json/__init__.py @@ -72,7 +72,7 @@ from deephaven import dtypes from deephaven._wrapper import JObjectWrapper -from deephaven.time import to_j_instant +from deephaven.time import InstantLike, to_j_instant from deephaven._jpy import strict_cast @@ -1056,14 +1056,13 @@ def string_val( return JsonValue(builder.build()) -# TODO(deephaven-core#5269): Create deephaven.time time-type aliases def instant_val( allow_missing: bool = True, allow_null: bool = True, number_format: Literal[None, "s", "ms", "us", "ns"] = None, allow_decimal: bool = False, - on_missing: Optional[Any] = None, - on_null: Optional[Any] = None, + on_missing: Optional[InstantLike] = None, + on_null: Optional[InstantLike] = None, ) -> JsonValue: """Creates an Instant value. For example, the JSON string @@ -1110,8 +1109,8 @@ def instant_val( the epoch. When not set, a JSON string in the ISO-8601 format is expected. allow_decimal (bool): if the Instant value is allowed to be a JSON decimal type, default is False. Only valid when number_format is specified. - on_missing (Optional[Any]): the value to use when the JSON value is missing and allow_missing is True, default is None. - on_null (Optional[Any]): the value to use when the JSON value is null and allow_null is True, default is None. + on_missing (Optional[InstantLike]): the value to use when the JSON value is missing and allow_missing is True, default is None. + on_null (Optional[InstantLike]): the value to use when the JSON value is null and allow_null is True, default is None. Returns: the Instant value diff --git a/py/server/deephaven/replay.py b/py/server/deephaven/replay.py index 78c8e018af1..b578c5e3d66 100644 --- a/py/server/deephaven/replay.py +++ b/py/server/deephaven/replay.py @@ -4,16 +4,12 @@ """ This module provides support for replaying historical data. """ -from typing import Union import jpy -import datetime -import numpy as np -import pandas as pd - -from deephaven import dtypes, DHError, time +from deephaven import DHError, time from deephaven._wrapper import JObjectWrapper from deephaven.table import Table +from deephaven.time import InstantLike _JReplayer = jpy.get_type("io.deephaven.engine.table.impl.replay.Replayer") @@ -27,14 +23,13 @@ class TableReplayer(JObjectWrapper): j_object_type = _JReplayer - def __init__(self, start_time: Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp], - end_time: Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp]): + def __init__(self, start_time: InstantLike, end_time: InstantLike): """Initializes the replayer. Args: - start_time (Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp]): + start_time (InstantLike): replay start time. Integer values are nanoseconds since the Epoch. - end_time (Union[dtypes.Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp]): + end_time (InstantLike): replay end time. Integer values are nanoseconds since the Epoch. Raises: diff --git a/py/server/deephaven/table_factory.py b/py/server/deephaven/table_factory.py index 02316928573..3b425583ee6 100644 --- a/py/server/deephaven/table_factory.py +++ b/py/server/deephaven/table_factory.py @@ -4,20 +4,19 @@ """ This module provides various ways to make a Deephaven table. """ -import datetime from typing import Callable, List, Dict, Any, Union, Sequence, Tuple, Mapping, Optional import jpy -import numpy as np import pandas as pd -from deephaven import execution_context, DHError, time +from deephaven import execution_context, DHError from deephaven._wrapper import JObjectWrapper from deephaven.column import InputColumn -from deephaven.dtypes import DType, Duration, Instant +from deephaven.dtypes import DType from deephaven.execution_context import ExecutionContext from deephaven.jcompat import j_lambda, j_list_to_list, to_sequence from deephaven.table import Table, TableDefinition, TableDefinitionLike +from deephaven.time import DurationLike, InstantLike, to_j_duration, to_j_instant from deephaven.update_graph import auto_locking_ctx _JTableFactory = jpy.get_type("io.deephaven.engine.table.TableFactory") @@ -53,16 +52,16 @@ def empty_table(size: int) -> Table: raise DHError(e, "failed to create an empty table.") from e -def time_table(period: Union[Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta], - start_time: Union[None, Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp] = None, +def time_table(period: DurationLike, + start_time: Optional[InstantLike] = None, blink_table: bool = False) -> Table: """Creates a table that adds a new row on a regular interval. Args: - period (Union[dtypes.Duration, int, str, datetime.timedelta, np.timedelta64, pd.Timedelta]): + period (DurationLike): time interval between new row additions, can be expressed as an integer in nanoseconds, a time interval string, e.g. "PT00:00:00.001" or "PT1s", or other time duration types. - start_time (Union[None, Instant, int, str, datetime.datetime, np.datetime64, pd.Timestamp], optional): + start_time (Optional[InstantLike]): start time for adding new rows, defaults to None which means use the current time as the start time. blink_table (bool, optional): if the time table should be a blink table, defaults to False @@ -76,14 +75,13 @@ def time_table(period: Union[Duration, int, str, datetime.timedelta, np.timedelt try: builder = _JTableTools.timeTableBuilder() - if not isinstance(period, str) and not isinstance(period, int): - period = time.to_j_duration(period) + if period is None: + raise ValueError("period must be specified") - builder.period(period) + builder.period(to_j_duration(period)) if start_time: - start_time = time.to_j_instant(start_time) - builder.startTime(start_time) + builder.startTime(to_j_instant(start_time)) if blink_table: builder.blinkTable(blink_table) diff --git a/py/server/deephaven/time.py b/py/server/deephaven/time.py index 208dc25c383..5868392c0c8 100644 --- a/py/server/deephaven/time.py +++ b/py/server/deephaven/time.py @@ -29,6 +29,50 @@ _NANOS_PER_SECOND = 1000000000 _NANOS_PER_MICRO = 1000 +if sys.version_info >= (3, 10): + from typing import TypeAlias # novermin + + TimeZoneLike : TypeAlias = Union[TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp] + """A Union representing objects that can be coerced into a TimeZone.""" + + LocalDateLike : TypeAlias = Union[LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp] + """A Union representing objects that can be coerced into a LocalDate.""" + + LocalTimeLike : TypeAlias = Union[LocalTime, int, str, datetime.time, datetime.datetime, numpy.datetime64, pandas.Timestamp] + """A Union representing objects that can be coerced into a LocalTime.""" + + InstantLike : TypeAlias = Union[Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp] + """A Union representing objects that can be coerced into an Instant.""" + + ZonedDateTimeLike : TypeAlias = Union[ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp] + """A Union representing objects that can be coerced into a ZonedDateTime.""" + + DurationLike : TypeAlias = Union[Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta] + """A Union representing objects that can be coerced into a Duration.""" + + PeriodLike : TypeAlias = Union[Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta] + """A Union representing objects that can be coerced into a Period.""" +else: + TimeZoneLike = Union[TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp] + """A Union representing objects that can be coerced into a TimeZone.""" + + LocalDateLike = Union[LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp] + """A Union representing objects that can be coerced into a LocalDate.""" + + LocalTimeLike = Union[LocalTime, int, str, datetime.time, datetime.datetime, numpy.datetime64, pandas.Timestamp] + """A Union representing objects that can be coerced into a LocalTime.""" + + InstantLike = Union[Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp] + """A Union representing objects that can be coerced into an Instant.""" + + ZonedDateTimeLike = Union[ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp] + """A Union representing objects that can be coerced into a ZonedDateTime.""" + + DurationLike = Union[Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta] + """A Union representing objects that can be coerced into a Duration.""" + + PeriodLike = Union[Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta] + """A Union representing objects that can be coerced into a Period.""" # region Clock @@ -223,15 +267,14 @@ def _tzinfo_to_j_time_zone(tzi: datetime.tzinfo) -> TimeZone: raise TypeError(f"Unsupported conversion: {str(type(tzi))} -> TimeZone\n\tDetails:\n\t{details}") -def to_j_time_zone(tz: Union[None, TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp]) -> \ - Optional[TimeZone]: +def to_j_time_zone(tz: Optional[TimeZoneLike]) -> Optional[TimeZone]: """ Converts a time zone value to a Java TimeZone. Time zone values can be None, a Java TimeZone, a string, a datetime.tzinfo, a datetime.datetime, or a pandas.Timestamp. Args: - tz (Union[None, TimeZone, str, datetime.tzinfo, datetime.datetime, pandas.Timestamp]): A time zone value. + tz (Optional[TimeZoneLike]): A time zone value. If None is provided, None is returned. If a string is provided, it is parsed as a time zone name. @@ -266,8 +309,7 @@ def to_j_time_zone(tz: Union[None, TimeZone, str, datetime.tzinfo, datetime.date raise DHError(e) from e -def to_j_local_date(dt: Union[None, LocalDate, str, datetime.date, datetime.datetime, - numpy.datetime64, pandas.Timestamp]) -> Optional[LocalDate]: +def to_j_local_date(dt: Optional[LocalDateLike]) -> Optional[LocalDate]: """ Converts a date time value to a Java LocalDate. Date time values can be None, a Java LocalDate, a string, a datetime.date, a datetime.datetime, @@ -276,8 +318,7 @@ def to_j_local_date(dt: Union[None, LocalDate, str, datetime.date, datetime.date Date strings can be formatted according to the ISO 8601 date time format as 'YYYY-MM-DD'. Args: - dt (Union[None, LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp]): - A date time value. If None is provided, None is returned. + dt (Optional[LocalDateLike]): A date time value. If None is provided, None is returned. Returns: LocalDate @@ -305,8 +346,7 @@ def to_j_local_date(dt: Union[None, LocalDate, str, datetime.date, datetime.date raise DHError(e) from e -def to_j_local_time(dt: Union[None, LocalTime, int, str, datetime.time, datetime.datetime, - numpy.datetime64, pandas.Timestamp]) -> Optional[LocalTime]: +def to_j_local_time(dt: Optional[LocalTimeLike]) -> Optional[LocalTime]: """ Converts a date time value to a Java LocalTime. Date time values can be None, a Java LocalTime, an int, a string, a datetime.time, a datetime.datetime, @@ -317,8 +357,7 @@ def to_j_local_time(dt: Union[None, LocalTime, int, str, datetime.time, datetime Time strings can be formatted as 'hh:mm:ss[.nnnnnnnnn]'. Args: - dt (Union[None, LocalTime, int, str, datetime.time, datetime.datetime, numpy.datetime64, pandas.Timestamp]): - A date time value. If None is provided, None is returned. + dt (Optional[LocalTimeLike]): A date time value. If None is provided, None is returned. Returns: LocalTime @@ -351,8 +390,7 @@ def to_j_local_time(dt: Union[None, LocalTime, int, str, datetime.time, datetime raise DHError(e) from e -def to_j_instant(dt: Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]) -> \ - Optional[Instant]: +def to_j_instant(dt: Optional[InstantLike]) -> Optional[Instant]: """ Converts a date time value to a Java Instant. Date time values can be None, a Java Instant, an int, a string, a datetime.datetime, @@ -366,8 +404,7 @@ def to_j_instant(dt: Union[None, Instant, int, str, datetime.datetime, numpy.dat from the Epoch. Expected date ranges are used to infer the units. Args: - dt (Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]): A date time value. - If None is provided, None is returned. + dt (Optional[InstantLike]): A date time value. If None is provided, None is returned. Returns: Instant, TypeError @@ -403,8 +440,7 @@ def to_j_instant(dt: Union[None, Instant, int, str, datetime.datetime, numpy.dat raise DHError(e) from e -def to_j_zdt(dt: Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]) -> \ - Optional[ZonedDateTime]: +def to_j_zdt(dt: Optional[ZonedDateTimeLike]) -> Optional[ZonedDateTime]: """ Converts a date time value to a Java ZonedDateTime. Date time values can be None, a Java ZonedDateTime, a string, a datetime.datetime, @@ -419,8 +455,7 @@ def to_j_zdt(dt: Union[None, ZonedDateTime, str, datetime.datetime, numpy.dateti Converting a numpy.datetime64 to a ZonedDateTime will use the Deephaven default time zone. Args: - dt (Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]): - A date time value. If None is provided, None is returned. + dt (Optional[ZonedDateTimeLike]): A date time value. If None is provided, None is returned. Returns: ZonedDateTime @@ -455,8 +490,7 @@ def to_j_zdt(dt: Union[None, ZonedDateTime, str, datetime.datetime, numpy.dateti raise DHError(e) from e -def to_j_duration(dt: Union[None, Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]) -> \ - Optional[Duration]: +def to_j_duration(dt: Optional[DurationLike]) -> Optional[Duration]: """ Converts a time duration value to a Java Duration, which is a unit of time in terms of clock time (24-hour days, hours, minutes, seconds, and nanoseconds). @@ -480,8 +514,7 @@ def to_j_duration(dt: Union[None, Duration, int, str, datetime.timedelta, numpy. | "-PT-6H+3M" -- parses as "+6 hours and -3 minutes" Args: - dt (Union[None, Duration, int, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]): - A time duration value. If None is provided, None is returned. + dt (Optional[DurationLike]): A time duration value. If None is provided, None is returned. Returns: Duration @@ -515,8 +548,7 @@ def to_j_duration(dt: Union[None, Duration, int, str, datetime.timedelta, numpy. raise DHError(e) from e -def to_j_period(dt: Union[None, Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]) -> \ - Optional[Period]: +def to_j_period(dt: Optional[PeriodLike]) -> Optional[Period]: """ Converts a time duration value to a Java Period, which is a unit of time in terms of calendar time (days, weeks, months, years, etc.). @@ -537,8 +569,7 @@ def to_j_period(dt: Union[None, Period, str, datetime.timedelta, numpy.timedelta | "-P1Y2M" -- -1 Year, -2 Months Args: - dt (Union[None, Period, str, datetime.timedelta, numpy.timedelta64, pandas.Timedelta]): - A Python period or period string. If None is provided, None is returned. + dt (Optional[PeriodLike]): A Python period or period string. If None is provided, None is returned. Returns: Period diff --git a/py/server/tests/test_table_factory.py b/py/server/tests/test_table_factory.py index fe66ba992ef..c4fb1cbea36 100644 --- a/py/server/tests/test_table_factory.py +++ b/py/server/tests/test_table_factory.py @@ -93,10 +93,13 @@ def test_time_table_blink(self): def test_time_table_error(self): with self.assertRaises(DHError) as cm: - t = time_table("PT00:0a:01") + time_table("PT00:0a:01") self.assertIn("DateTimeParseException", cm.exception.root_cause) + with self.assertRaises(DHError): + time_table(None) + def test_merge(self): t1 = self.test_table.update(formulas=["Timestamp=epochNanosToInstant(0L)"]) t2 = self.test_table.update(formulas=["Timestamp=nowSystem()"])