Module astrapy.data_types

Expand source code
# Copyright DataStax, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from astrapy.data_types.data_api_date import DataAPIDate
from astrapy.data_types.data_api_duration import DataAPIDuration
from astrapy.data_types.data_api_map import DataAPIMap
from astrapy.data_types.data_api_set import DataAPISet
from astrapy.data_types.data_api_time import DataAPITime
from astrapy.data_types.data_api_timestamp import DataAPITimestamp
from astrapy.data_types.data_api_vector import DataAPIVector

__all__ = [
    "DataAPITimestamp",
    "DataAPIVector",
    "DataAPIDate",
    "DataAPIDuration",
    "DataAPIMap",
    "DataAPISet",
    "DataAPITime",
]

Sub-modules

astrapy.data_types.data_api_date
astrapy.data_types.data_api_duration
astrapy.data_types.data_api_map
astrapy.data_types.data_api_set
astrapy.data_types.data_api_time
astrapy.data_types.data_api_timestamp
astrapy.data_types.data_api_vector

Classes

class DataAPIDate (year: int, month: int, day: int)

A value expressing a date, composed of a year, a month and a day, suitable for working with the "date" table column type.

This class is designed to losslessly express the full date range the Data API supports, overcoming the year range limitation of Python's standard-library date (i.e. 1AD to 9999AD).

DataAPIDate objects are meant to easily work in the context of the Data API, hence its conversion methods from/to a string assumed the particular format employed by the API.

The class also offers conversion methods from/to the regular Python datetime.date; however these may fail if the year falls outside of the range supported by the latter.

Args

year
the year for the date. Any integer is accepted.
month
an integer number in the 1-12 range.
day
an integer number in a range between 1 and the number of days in the chosen month (whose value depends on the month and, in the case of February, on whether the year is a leap year).

Example

>>> from astrapy.data_types import DataAPIDate
>>> date1 = DataAPIDate(2024, 12, 31)
>>> date2 = DataAPIDate(-44, 3, 15)
>>> date2
DataAPIDate(-44, 3, 15)
>>> date1.year
2024
Expand source code
@dataclass
class DataAPIDate:
    """
    A value expressing a date, composed of a year, a month and a day, suitable
    for working with the "date" table column type.

    This class is designed to losslessly express the full date range the Data API
    supports, overcoming the year range limitation of Python's standard-library
    date (i.e. 1AD to 9999AD).

    DataAPIDate objects are meant to easily work in the context of the Data API,
    hence its conversion methods from/to a string assumed the particular format
    employed by the API.

    The class also offers conversion methods from/to the regular
    Python `datetime.date`; however these may fail if the year falls outside
    of the range supported by the latter.

    Args:
        year: the year for the date. Any integer is accepted.
        month: an integer number in the 1-12 range.
        day: an integer number in a range between 1 and the number of days
            in the chosen month (whose value depends on the month and, in the case
            of February, on whether the year is a leap year).

    Example:
        >>> from astrapy.data_types import DataAPIDate
        >>> date1 = DataAPIDate(2024, 12, 31)
        >>> date2 = DataAPIDate(-44, 3, 15)
        >>> date2
        DataAPIDate(-44, 3, 15)
        >>> date1.year
        2024
    """

    year: int
    month: int
    day: int

    def __init__(self, year: int, month: int, day: int):
        _fail_reason = _validate_date(
            year=year,
            month=month,
            day=day,
        )
        if _fail_reason:
            raise ValueError(f"Invalid date arguments: {_fail_reason}.")
        self.year = year
        self.month = month
        self.day = day

    def __hash__(self) -> int:
        return hash((self.year, self.month, self.day))

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}({self.year}, {self.month}, {self.day})"

    def __str__(self) -> str:
        return self.to_string()

    def __reduce__(self) -> tuple[type, tuple[int, int, int]]:
        return self.__class__, (self.year, self.month, self.day)

    def __le__(self, other: Any) -> bool:
        if isinstance(other, DataAPIDate):
            return self._to_tuple() <= other._to_tuple()
        elif isinstance(other, datetime.date):
            return self.__le__(DataAPIDate.from_date(other))
        else:
            return NotImplemented

    def __lt__(self, other: Any) -> bool:
        if isinstance(other, DataAPIDate):
            return self._to_tuple() < other._to_tuple()
        elif isinstance(other, datetime.date):
            return self.__lt__(DataAPIDate.from_date(other))
        else:
            return NotImplemented

    def __ge__(self, other: Any) -> bool:
        if isinstance(other, DataAPIDate):
            return self._to_tuple() >= other._to_tuple()
        elif isinstance(other, datetime.date):
            return self.__ge__(DataAPIDate.from_date(other))
        else:
            return NotImplemented

    def __gt__(self, other: Any) -> bool:
        if isinstance(other, DataAPIDate):
            return self._to_tuple() > other._to_tuple()
        elif isinstance(other, datetime.date):
            return self.__gt__(DataAPIDate.from_date(other))
        else:
            return NotImplemented

    def _to_tuple(self) -> tuple[int, int, int]:
        return (self.year, self.month, self.day)

    def to_string(self) -> str:
        """
        Express the date as a string according to the Data API convention,
        including the presence of a sign, and the number of digits, for the year.

        Returns:
            a string, such as "2024-12-31", formatted in a way suitable to be
            in a Data API payload.

        Example:
            >>> from astrapy.data_types import DataAPIDate
            >>> date1 = DataAPIDate(2024, 12, 31)
            >>> date2 = DataAPIDate(-44, 3, 15)
            >>> date1.to_string()
            '2024-12-31'
            >>> date2.to_string()
            '-0044-03-15'
        """

        # the year part requires care around the sign and number of digits
        y = self.year
        year_str: str
        if y > 9999:
            year_str = f"{y:+}"
        elif y >= 0:
            year_str = f"{y:04}"
        else:
            year_str = f"{y:+05}"
        return f"{year_str}-{self.month:02}-{self.day:02}"

    def to_date(self) -> datetime.date:
        """
        Attempt to convert the date into a Python standard-library `datetime.date`.
        This operation may fail with a ValueError if the DataAPIDate's year falls
        outside of the range supported by the standard library.

        Returns:
            a `datetime.date` object if the conversion is successful.

        Example:
            >>> from astrapy.data_types import DataAPIDate
            >>> date1 = DataAPIDate(2024, 12, 31)
            >>> date2 = DataAPIDate(-44, 3, 15)
            >>> date1.to_date()
            datetime.date(2024, 12, 31)
            >>> date2.to_date()
            Traceback (most recent call last):
              [...]
            ValueError: year -44 is out of range
        """

        return datetime.date(*self._to_tuple())

    @staticmethod
    def from_date(dt: datetime.date) -> DataAPIDate:
        """
        Convert a Python standard-library date into a DataAPIDate.

        Args:
            dt: a `datetime.date` object.

        Returns:
            a DataAPIDate, corresponding to the provided input.

        Example:
            >>> from datetime import date
            >>>
            >>> from astrapy.data_types import DataAPIDate
            >>>
            >>> std_date = date(2024, 12, 31)
            >>> DataAPIDate.from_date(std_date)
            DataAPIDate(2024, 12, 31)
        """

        return DataAPIDate(
            year=dt.year,
            month=dt.month,
            day=dt.day,
        )

    @staticmethod
    def from_string(date_string: str) -> DataAPIDate:
        """
        Convert a string into a DataAPIDate, provided the string represents one
        according to the Data API format conventions. If the format is unrecognized,
        a ValueError is raised.

        Args:
            date_string: a valid string expressing a date as per Data API conventions.

        Returns:
            a DataAPIDate corresponding to the provided input.

        Example:
            >>> from astrapy.data_types import DataAPIDate
            >>>
            >>> DataAPIDate.from_string("2024-12-31")
            DataAPIDate(2024, 12, 31)
            >>> DataAPIDate.from_string("-0044-03-15")
            DataAPIDate(-44, 3, 15)
            >>> DataAPIDate.from_string("1905-13-15")
            Traceback (most recent call last):
                [...]
            ValueError: Cannot parse '1905-13-15' into a valid date: illegal month [...]
        """

        match = DATE_PARSE_PATTERN.match(date_string)
        if match:
            # the year string has additional constraints besides the regexp:
            year_str = match[1]
            if year_str and year_str[0] == "+":
                if len(year_str[1:]) <= 4:
                    raise ValueError(
                        f"Cannot parse '{date_string}' into a valid timestamp: "
                        "four-digit positive year should bear no plus sign. "
                        f"{DATE_FORMAT_DESC}"
                    )
            if len(year_str) > 4 and year_str[0] not in {"+", "-"}:
                raise ValueError(
                    f"Cannot parse '{date_string}' into a valid timestamp: "
                    "years with more than four digits should bear a leading sign. "
                    f"{DATE_FORMAT_DESC}"
                )
            year = int(year_str)
            if year == 0 and year_str[0] == "-":
                raise ValueError(
                    f"Cannot parse '{date_string}' into a valid timestamp: "
                    "year zero should be provided as '0000' without leading sign. "
                    f"{DATE_FORMAT_DESC}"
                )
            month = int(match[2])
            day = int(match[3])
            _fail_reason = _validate_date(year=year, month=month, day=day)
            if _fail_reason:
                raise ValueError(
                    f"Cannot parse '{date_string}' into a valid date: "
                    f"{_fail_reason}. {DATE_FORMAT_DESC}"
                )
            return DataAPIDate(year=year, month=month, day=day)
        else:
            raise ValueError(
                f"Cannot parse '{date_string}' into a valid date "
                f"(unrecognized format). {DATE_FORMAT_DESC}"
            )

Class variables

var day : int
var month : int
var year : int

Static methods

def from_date(dt: datetime.date) ‑> DataAPIDate

Convert a Python standard-library date into a DataAPIDate.

Args

dt
a datetime.date object.

Returns

a DataAPIDate, corresponding to the provided input.

Example

>>> from datetime import date
>>>
>>> from astrapy.data_types import DataAPIDate
>>>
>>> std_date = date(2024, 12, 31)
>>> DataAPIDate.from_date(std_date)
DataAPIDate(2024, 12, 31)
Expand source code
@staticmethod
def from_date(dt: datetime.date) -> DataAPIDate:
    """
    Convert a Python standard-library date into a DataAPIDate.

    Args:
        dt: a `datetime.date` object.

    Returns:
        a DataAPIDate, corresponding to the provided input.

    Example:
        >>> from datetime import date
        >>>
        >>> from astrapy.data_types import DataAPIDate
        >>>
        >>> std_date = date(2024, 12, 31)
        >>> DataAPIDate.from_date(std_date)
        DataAPIDate(2024, 12, 31)
    """

    return DataAPIDate(
        year=dt.year,
        month=dt.month,
        day=dt.day,
    )
def from_string(date_string: str) ‑> DataAPIDate

Convert a string into a DataAPIDate, provided the string represents one according to the Data API format conventions. If the format is unrecognized, a ValueError is raised.

Args

date_string
a valid string expressing a date as per Data API conventions.

Returns

a DataAPIDate corresponding to the provided input.

Example

>>> from astrapy.data_types import DataAPIDate
>>>
>>> DataAPIDate.from_string("2024-12-31")
DataAPIDate(2024, 12, 31)
>>> DataAPIDate.from_string("-0044-03-15")
DataAPIDate(-44, 3, 15)
>>> DataAPIDate.from_string("1905-13-15")
Traceback (most recent call last):
    [...]
ValueError: Cannot parse '1905-13-15' into a valid date: illegal month [...]
Expand source code
@staticmethod
def from_string(date_string: str) -> DataAPIDate:
    """
    Convert a string into a DataAPIDate, provided the string represents one
    according to the Data API format conventions. If the format is unrecognized,
    a ValueError is raised.

    Args:
        date_string: a valid string expressing a date as per Data API conventions.

    Returns:
        a DataAPIDate corresponding to the provided input.

    Example:
        >>> from astrapy.data_types import DataAPIDate
        >>>
        >>> DataAPIDate.from_string("2024-12-31")
        DataAPIDate(2024, 12, 31)
        >>> DataAPIDate.from_string("-0044-03-15")
        DataAPIDate(-44, 3, 15)
        >>> DataAPIDate.from_string("1905-13-15")
        Traceback (most recent call last):
            [...]
        ValueError: Cannot parse '1905-13-15' into a valid date: illegal month [...]
    """

    match = DATE_PARSE_PATTERN.match(date_string)
    if match:
        # the year string has additional constraints besides the regexp:
        year_str = match[1]
        if year_str and year_str[0] == "+":
            if len(year_str[1:]) <= 4:
                raise ValueError(
                    f"Cannot parse '{date_string}' into a valid timestamp: "
                    "four-digit positive year should bear no plus sign. "
                    f"{DATE_FORMAT_DESC}"
                )
        if len(year_str) > 4 and year_str[0] not in {"+", "-"}:
            raise ValueError(
                f"Cannot parse '{date_string}' into a valid timestamp: "
                "years with more than four digits should bear a leading sign. "
                f"{DATE_FORMAT_DESC}"
            )
        year = int(year_str)
        if year == 0 and year_str[0] == "-":
            raise ValueError(
                f"Cannot parse '{date_string}' into a valid timestamp: "
                "year zero should be provided as '0000' without leading sign. "
                f"{DATE_FORMAT_DESC}"
            )
        month = int(match[2])
        day = int(match[3])
        _fail_reason = _validate_date(year=year, month=month, day=day)
        if _fail_reason:
            raise ValueError(
                f"Cannot parse '{date_string}' into a valid date: "
                f"{_fail_reason}. {DATE_FORMAT_DESC}"
            )
        return DataAPIDate(year=year, month=month, day=day)
    else:
        raise ValueError(
            f"Cannot parse '{date_string}' into a valid date "
            f"(unrecognized format). {DATE_FORMAT_DESC}"
        )

Methods

def to_date(self) ‑> datetime.date

Attempt to convert the date into a Python standard-library datetime.date. This operation may fail with a ValueError if the DataAPIDate's year falls outside of the range supported by the standard library.

Returns

a datetime.date object if the conversion is successful.

Example

>>> from astrapy.data_types import DataAPIDate
>>> date1 = DataAPIDate(2024, 12, 31)
>>> date2 = DataAPIDate(-44, 3, 15)
>>> date1.to_date()
datetime.date(2024, 12, 31)
>>> date2.to_date()
Traceback (most recent call last):
  [...]
ValueError: year -44 is out of range
Expand source code
def to_date(self) -> datetime.date:
    """
    Attempt to convert the date into a Python standard-library `datetime.date`.
    This operation may fail with a ValueError if the DataAPIDate's year falls
    outside of the range supported by the standard library.

    Returns:
        a `datetime.date` object if the conversion is successful.

    Example:
        >>> from astrapy.data_types import DataAPIDate
        >>> date1 = DataAPIDate(2024, 12, 31)
        >>> date2 = DataAPIDate(-44, 3, 15)
        >>> date1.to_date()
        datetime.date(2024, 12, 31)
        >>> date2.to_date()
        Traceback (most recent call last):
          [...]
        ValueError: year -44 is out of range
    """

    return datetime.date(*self._to_tuple())
def to_string(self) ‑> str

Express the date as a string according to the Data API convention, including the presence of a sign, and the number of digits, for the year.

Returns

a string, such as "2024-12-31", formatted in a way suitable to be in a Data API payload.

Example

>>> from astrapy.data_types import DataAPIDate
>>> date1 = DataAPIDate(2024, 12, 31)
>>> date2 = DataAPIDate(-44, 3, 15)
>>> date1.to_string()
'2024-12-31'
>>> date2.to_string()
'-0044-03-15'
Expand source code
def to_string(self) -> str:
    """
    Express the date as a string according to the Data API convention,
    including the presence of a sign, and the number of digits, for the year.

    Returns:
        a string, such as "2024-12-31", formatted in a way suitable to be
        in a Data API payload.

    Example:
        >>> from astrapy.data_types import DataAPIDate
        >>> date1 = DataAPIDate(2024, 12, 31)
        >>> date2 = DataAPIDate(-44, 3, 15)
        >>> date1.to_string()
        '2024-12-31'
        >>> date2.to_string()
        '-0044-03-15'
    """

    # the year part requires care around the sign and number of digits
    y = self.year
    year_str: str
    if y > 9999:
        year_str = f"{y:+}"
    elif y >= 0:
        year_str = f"{y:04}"
    else:
        year_str = f"{y:+05}"
    return f"{year_str}-{self.month:02}-{self.day:02}"
class DataAPIDuration (signum: int, months: int, days: int, nanoseconds: int)

A "duration" value, suitable for working with the "duration" table column type.

Durations are an abstract notion: as such, they cannot be mapped into a precise, well-defined time span. In other words, their components cannot be referred one to another. Indeed, a 'month' is not a fixed amount of days, and likewise a 'day' is not a fixed amount of hours (due to daylight saving changes, there are days made of 23 or 25 hours).

For this reason, the DataAPIDuration class cannot really be mapped to a Python standard-library datetime.timedelta, unless in a restricted set of cases and/or with some approximations involved.

Args

months
an integer non-negative amount of months.
days
an integer non-negative amount of days.
nanoseconds
an integer non-negative amount of nanoseconds. This quantity encodes the whole sub-day component of the duration: for instance, a duration of 36 hours is expressed as 36 * 3600 * 1000000000 = 129600000000000 nanoseconds.
signum
an overall plus or minus sign, represented as either +1 or -1. This allows the encoding of negative durations.
Expand source code
@dataclass
class DataAPIDuration:
    """
    A "duration" value, suitable for working with the "duration" table column type.

    Durations are an abstract notion: as such, they cannot be mapped into a precise,
    well-defined time span. In other words, their components cannot be referred
    one to another. Indeed, a 'month' is not a fixed amount of days, and likewise
    a 'day' is not a fixed amount of hours (due to daylight saving changes, there are
    days made of 23 or 25 hours).

    For this reason, the DataAPIDuration class cannot really be mapped to a Python
    standard-library `datetime.timedelta`, unless in a restricted set of cases and/or
    with some approximations involved.

    Args:
        months: an integer non-negative amount of months.
        days: an integer non-negative amount of days.
        nanoseconds: an integer non-negative amount of nanoseconds.
            This quantity encodes the whole sub-day component of the
            duration: for instance, a duration of 36 hours is expressed as
            36 * 3600 * 1000000000 = 129600000000000 nanoseconds.
        signum: an overall plus or minus sign, represented as either +1 or -1.
            This allows the encoding of negative durations.
    """

    months: int
    days: int
    nanoseconds: int
    signum: int

    def __init__(
        self,
        signum: int,
        months: int,
        days: int,
        nanoseconds: int,
    ) -> None:
        if months < 0 or days < 0 or nanoseconds < 0:
            raise ValueError(
                "months, days, nanoseconds cannot be negative. Use overall 'signum'."
            )
        if signum not in {+1, -1}:
            raise ValueError("signum must be either +1 or -1.")
        self.months = months
        self.days = days
        self.nanoseconds = nanoseconds
        self.signum = signum

    def __repr__(self) -> str:
        def irepr(val: int) -> str:
            if val != 0 and self.signum < 0:
                return f"-{val}"
            else:
                return f"{val}"

        inner_desc = (
            f"months={irepr(self.months)}, days={irepr(self.days)}, "
            f"nanoseconds={irepr(self.nanoseconds)}"
        )
        return f"{self.__class__.__name__}({inner_desc})"

    def __str__(self) -> str:
        return self.to_string()

    def __reduce__(self) -> tuple[type, tuple[int, int, int, int]]:
        return self.__class__, (self.signum, self.months, self.days, self.nanoseconds)

    def __hash__(self) -> int:
        return hash((self.signum, self.months, self.days, self.nanoseconds))

    @staticmethod
    def from_string(duration_string: str) -> DataAPIDuration:
        """
        Parse a string, expressed according to the Data API ISO-8601 format, into a
        DataAPIDuration. If the format is not recognized, a ValueError is raised.

        Args:
            duration_string: a string compliant to the Data API ISO-8601 specification
            for durations.

        Returns:
            a DataAPIDuration corresponding to the provided input.

        Example:
            >>> from astrapy.data_types import DataAPIDuration
            >>>
            >>> DataAPIDuration.from_string("P3Y6M4DT12H30M5S")
            DataAPIDuration(months=42, days=4, nanoseconds=45005000000000)
            >>> DataAPIDuration.from_string("PT12H")
            DataAPIDuration(months=0, days=0, nanoseconds=43200000000000)
            >>> DataAPIDuration.from_string("PT12H11X")
            Traceback (most recent call last):
              [...]
            ValueError: Invalid fraction-of-day component for a duration string ...
        """

        si, mo, da, ns = _parse_std_duration_string(duration_string)
        return DataAPIDuration(
            signum=si,
            months=mo,
            days=da,
            nanoseconds=ns,
        )

    def to_string(self) -> str:
        """
        Convert the DataAPIDuration to a string according to the Data API
        ISO-8601 standard.

        Returns:
            a string expressing the time value in a way compatible with the Data API
            conventions.

        Example:
            >>> from astrapy.data_types import DataAPIDuration
            >>>
            >>> dd1 = DataAPIDuration(1, months=42, days=4, nanoseconds=45005000000000)
            >>> dd1.to_string()
            'P3Y6M4DT12H30M5S'
            >>> dd2 = DataAPIDuration(1, months=0, days=0, nanoseconds=43200000000000)
            >>> dd2.to_string()
            'PT12H'
        """

        return _build_std_duration_string(
            signum=self.signum,
            months=self.months,
            days=self.days,
            nanoseconds=self.nanoseconds,
        )

    @staticmethod
    def from_c_string(duration_string: str) -> DataAPIDuration:
        """
        Parse a string, expressed according to the Data API "Apache Cassandra(R)
        notation", into a DataAPIDuration.
        If the format is not recognized, a ValueError is raised.

        Args:
            duration_string: a string compliant to the Data API "Apache Cassandra(R)
            notation" for durations.

        Returns:
            a DataAPIDuration corresponding to the provided input.

        Example:
            >>> from astrapy.data_types import DataAPIDuration
            >>>
            >>> DataAPIDuration.from_c_string("12y3mo1d")
            DataAPIDuration(months=147, days=1, nanoseconds=0)
            >>> DataAPIDuration.from_c_string("12y3mo1d12h30m5s")
            DataAPIDuration(months=147, days=1, nanoseconds=45005000000000)
            >>> DataAPIDuration.from_c_string("-4h1978us")
            DataAPIDuration(months=0, days=0, nanoseconds=-14400001978000)
            >>> DataAPIDuration.from_c_string("0h0m")
            DataAPIDuration(months=0, days=0, nanoseconds=0)
            >>> DataAPIDuration.from_c_string("1h1y")
            Traceback (most recent call last):
              [...]
            ValueError: Unit 'y' cannot follow smaller units in literal for ...
        """

        si, mo, da, ns = _parse_c_duration_string(duration_string)
        return DataAPIDuration(
            signum=si,
            months=mo,
            days=da,
            nanoseconds=ns,
        )

    def to_c_string(self) -> str:
        """
        Convert the DataAPIDuration to a string according to the Data API
        "Apache Cassandra(R) notation".

        Returns:
            a string expressing the time value in a way compatible with the Data API
            conventions.

        Example:
            >>> from astrapy.data_types import DataAPIDuration
            >>>
            >>> dd1 = DataAPIDuration(1, months=147, days=1, nanoseconds=0)
            >>> dd1.to_c_string()
            '12y3mo1d'
            >>> dd2 = DataAPIDuration(1, months=147, days=1, nanoseconds=45005000000000)
            >>> dd2.to_c_string()
            '12y3mo1d12h30m5s'
            >>> dd3 = DataAPIDuration(-1, months=0, days=0, nanoseconds=14400001978000)
            >>> dd3.to_c_string()
            '-4h1ms978us'
            >>> dd4 = DataAPIDuration(1, months=0, days=0, nanoseconds=0)
            >>> dd4.to_c_string()
            '0s'
        """

        return _build_c_duration_string(
            signum=self.signum,
            months=self.months,
            days=self.days,
            nanoseconds=self.nanoseconds,
        )

    @staticmethod
    def from_timedelta(td: datetime.timedelta) -> DataAPIDuration:
        """
        Construct a DataAPIDuration from a Python standard-library `datetime.timedelta`.

        Due to the intrinsic difference between the notions of a DataAPIDuration and
        the timedelta, the latter - a definite time span - is only ever resulting
        in a duration with a nonzero "nanoseconds" component.

        Args:
            dt: a `datetime.timedelta` value.

        Returns:
            A DataAPIDuration corresponding value, with null month and day components
            by construction.

        Example:
            >>> from datetime import timedelta
            >>>
            >>> from astrapy.data_types import DataAPIDuration
            >>>
            >>> DataAPIDuration.from_timedelta(
            ...     timedelta(days=1, hours=2, seconds=10.987)
            ... )
            DataAPIDuration(months=0, days=0, nanoseconds=93610987000000)
            >>> DataAPIDuration.from_timedelta(timedelta(hours=-4))
            DataAPIDuration(months=0, days=0, nanoseconds=-14400000000000)
        """

        # this conversion expresses a duration with sub-days component only,
        # since a 'timedelta' is a precise time span (as opposed to durations).
        total_nanoseconds = int(td.total_seconds() * 1000000000)
        if total_nanoseconds >= 0:
            return DataAPIDuration(
                signum=+1,
                months=0,
                days=0,
                nanoseconds=total_nanoseconds,
            )
        else:
            return DataAPIDuration(
                signum=-1,
                months=0,
                days=0,
                nanoseconds=-total_nanoseconds,
            )

    def to_timedelta(self) -> datetime.timedelta:
        """
        Convert a DataAPIDuration into a Python standard library `datetime.timedelta`.

        Due to the intrinsic difference between the notions of a DataAPIDuration and
        the timedelta, the conversion attempt raises an error if the starting
        DataAPIDuration has a nonzero number of months. For the same reason, a somewhat
        lossy conversion occurs for a nonzero number of days since the formal notion
        of a day is lost in favor of that of "a span of exactly 24 hours".

        Returns:
            a `datetime.timedelta` value corresponding to the origin DataAPIDuration
            in the best possible way.

        Example:
            >>> from astrapy.data_types import DataAPIDuration
            >>>
            >>> dd1 = DataAPIDuration(
            ...     signum=-1, months=0, days=0, nanoseconds=93610987000000
            ... )
            >>> dd1.to_timedelta()
            datetime.timedelta(days=-2, seconds=79189, microseconds=13000)
            >>> dd2 = DataAPIDuration(
            ...     signum=1, months=0, days=0, nanoseconds=14400000000000
            ... )
            >>> dd2.to_timedelta()
            datetime.timedelta(seconds=14400)
            >>> dd3 = DataAPIDuration(signum=1, months=19, days=0, nanoseconds=0)
            >>> dd3.to_timedelta()
            Traceback (most recent call last):
                [...]
            ValueError: Cannot convert a DataAPIDuration with nonzero months into ...
        """

        if self.months != 0:
            raise ValueError(
                "Cannot convert a DataAPIDuration with nonzero months into a timedelta."
            )
        return datetime.timedelta(
            days=self.signum * self.days,
            microseconds=self.signum * self.nanoseconds // 1000,
        )

Class variables

var days : int
var months : int
var nanoseconds : int
var signum : int

Static methods

def from_c_string(duration_string: str) ‑> DataAPIDuration

Parse a string, expressed according to the Data API "Apache Cassandra(R) notation", into a DataAPIDuration. If the format is not recognized, a ValueError is raised.

Args

duration_string
a string compliant to the Data API "Apache Cassandra(R)

notation" for durations.

Returns

a DataAPIDuration corresponding to the provided input.

Example

>>> from astrapy.data_types import DataAPIDuration
>>>
>>> DataAPIDuration.from_c_string("12y3mo1d")
DataAPIDuration(months=147, days=1, nanoseconds=0)
>>> DataAPIDuration.from_c_string("12y3mo1d12h30m5s")
DataAPIDuration(months=147, days=1, nanoseconds=45005000000000)
>>> DataAPIDuration.from_c_string("-4h1978us")
DataAPIDuration(months=0, days=0, nanoseconds=-14400001978000)
>>> DataAPIDuration.from_c_string("0h0m")
DataAPIDuration(months=0, days=0, nanoseconds=0)
>>> DataAPIDuration.from_c_string("1h1y")
Traceback (most recent call last):
  [...]
ValueError: Unit 'y' cannot follow smaller units in literal for ...
Expand source code
@staticmethod
def from_c_string(duration_string: str) -> DataAPIDuration:
    """
    Parse a string, expressed according to the Data API "Apache Cassandra(R)
    notation", into a DataAPIDuration.
    If the format is not recognized, a ValueError is raised.

    Args:
        duration_string: a string compliant to the Data API "Apache Cassandra(R)
        notation" for durations.

    Returns:
        a DataAPIDuration corresponding to the provided input.

    Example:
        >>> from astrapy.data_types import DataAPIDuration
        >>>
        >>> DataAPIDuration.from_c_string("12y3mo1d")
        DataAPIDuration(months=147, days=1, nanoseconds=0)
        >>> DataAPIDuration.from_c_string("12y3mo1d12h30m5s")
        DataAPIDuration(months=147, days=1, nanoseconds=45005000000000)
        >>> DataAPIDuration.from_c_string("-4h1978us")
        DataAPIDuration(months=0, days=0, nanoseconds=-14400001978000)
        >>> DataAPIDuration.from_c_string("0h0m")
        DataAPIDuration(months=0, days=0, nanoseconds=0)
        >>> DataAPIDuration.from_c_string("1h1y")
        Traceback (most recent call last):
          [...]
        ValueError: Unit 'y' cannot follow smaller units in literal for ...
    """

    si, mo, da, ns = _parse_c_duration_string(duration_string)
    return DataAPIDuration(
        signum=si,
        months=mo,
        days=da,
        nanoseconds=ns,
    )
def from_string(duration_string: str) ‑> DataAPIDuration

Parse a string, expressed according to the Data API ISO-8601 format, into a DataAPIDuration. If the format is not recognized, a ValueError is raised.

Args

duration_string
a string compliant to the Data API ISO-8601 specification

for durations.

Returns

a DataAPIDuration corresponding to the provided input.

Example

>>> from astrapy.data_types import DataAPIDuration
>>>
>>> DataAPIDuration.from_string("P3Y6M4DT12H30M5S")
DataAPIDuration(months=42, days=4, nanoseconds=45005000000000)
>>> DataAPIDuration.from_string("PT12H")
DataAPIDuration(months=0, days=0, nanoseconds=43200000000000)
>>> DataAPIDuration.from_string("PT12H11X")
Traceback (most recent call last):
  [...]
ValueError: Invalid fraction-of-day component for a duration string ...
Expand source code
@staticmethod
def from_string(duration_string: str) -> DataAPIDuration:
    """
    Parse a string, expressed according to the Data API ISO-8601 format, into a
    DataAPIDuration. If the format is not recognized, a ValueError is raised.

    Args:
        duration_string: a string compliant to the Data API ISO-8601 specification
        for durations.

    Returns:
        a DataAPIDuration corresponding to the provided input.

    Example:
        >>> from astrapy.data_types import DataAPIDuration
        >>>
        >>> DataAPIDuration.from_string("P3Y6M4DT12H30M5S")
        DataAPIDuration(months=42, days=4, nanoseconds=45005000000000)
        >>> DataAPIDuration.from_string("PT12H")
        DataAPIDuration(months=0, days=0, nanoseconds=43200000000000)
        >>> DataAPIDuration.from_string("PT12H11X")
        Traceback (most recent call last):
          [...]
        ValueError: Invalid fraction-of-day component for a duration string ...
    """

    si, mo, da, ns = _parse_std_duration_string(duration_string)
    return DataAPIDuration(
        signum=si,
        months=mo,
        days=da,
        nanoseconds=ns,
    )
def from_timedelta(td: datetime.timedelta) ‑> DataAPIDuration

Construct a DataAPIDuration from a Python standard-library datetime.timedelta.

Due to the intrinsic difference between the notions of a DataAPIDuration and the timedelta, the latter - a definite time span - is only ever resulting in a duration with a nonzero "nanoseconds" component.

Args

dt
a datetime.timedelta value.

Returns

A DataAPIDuration corresponding value, with null month and day components by construction.

Example

>>> from datetime import timedelta
>>>
>>> from astrapy.data_types import DataAPIDuration
>>>
>>> DataAPIDuration.from_timedelta(
...     timedelta(days=1, hours=2, seconds=10.987)
... )
DataAPIDuration(months=0, days=0, nanoseconds=93610987000000)
>>> DataAPIDuration.from_timedelta(timedelta(hours=-4))
DataAPIDuration(months=0, days=0, nanoseconds=-14400000000000)
Expand source code
@staticmethod
def from_timedelta(td: datetime.timedelta) -> DataAPIDuration:
    """
    Construct a DataAPIDuration from a Python standard-library `datetime.timedelta`.

    Due to the intrinsic difference between the notions of a DataAPIDuration and
    the timedelta, the latter - a definite time span - is only ever resulting
    in a duration with a nonzero "nanoseconds" component.

    Args:
        dt: a `datetime.timedelta` value.

    Returns:
        A DataAPIDuration corresponding value, with null month and day components
        by construction.

    Example:
        >>> from datetime import timedelta
        >>>
        >>> from astrapy.data_types import DataAPIDuration
        >>>
        >>> DataAPIDuration.from_timedelta(
        ...     timedelta(days=1, hours=2, seconds=10.987)
        ... )
        DataAPIDuration(months=0, days=0, nanoseconds=93610987000000)
        >>> DataAPIDuration.from_timedelta(timedelta(hours=-4))
        DataAPIDuration(months=0, days=0, nanoseconds=-14400000000000)
    """

    # this conversion expresses a duration with sub-days component only,
    # since a 'timedelta' is a precise time span (as opposed to durations).
    total_nanoseconds = int(td.total_seconds() * 1000000000)
    if total_nanoseconds >= 0:
        return DataAPIDuration(
            signum=+1,
            months=0,
            days=0,
            nanoseconds=total_nanoseconds,
        )
    else:
        return DataAPIDuration(
            signum=-1,
            months=0,
            days=0,
            nanoseconds=-total_nanoseconds,
        )

Methods

def to_c_string(self) ‑> str

Convert the DataAPIDuration to a string according to the Data API "Apache Cassandra(R) notation".

Returns

a string expressing the time value in a way compatible with the Data API conventions.

Example

>>> from astrapy.data_types import DataAPIDuration
>>>
>>> dd1 = DataAPIDuration(1, months=147, days=1, nanoseconds=0)
>>> dd1.to_c_string()
'12y3mo1d'
>>> dd2 = DataAPIDuration(1, months=147, days=1, nanoseconds=45005000000000)
>>> dd2.to_c_string()
'12y3mo1d12h30m5s'
>>> dd3 = DataAPIDuration(-1, months=0, days=0, nanoseconds=14400001978000)
>>> dd3.to_c_string()
'-4h1ms978us'
>>> dd4 = DataAPIDuration(1, months=0, days=0, nanoseconds=0)
>>> dd4.to_c_string()
'0s'
Expand source code
def to_c_string(self) -> str:
    """
    Convert the DataAPIDuration to a string according to the Data API
    "Apache Cassandra(R) notation".

    Returns:
        a string expressing the time value in a way compatible with the Data API
        conventions.

    Example:
        >>> from astrapy.data_types import DataAPIDuration
        >>>
        >>> dd1 = DataAPIDuration(1, months=147, days=1, nanoseconds=0)
        >>> dd1.to_c_string()
        '12y3mo1d'
        >>> dd2 = DataAPIDuration(1, months=147, days=1, nanoseconds=45005000000000)
        >>> dd2.to_c_string()
        '12y3mo1d12h30m5s'
        >>> dd3 = DataAPIDuration(-1, months=0, days=0, nanoseconds=14400001978000)
        >>> dd3.to_c_string()
        '-4h1ms978us'
        >>> dd4 = DataAPIDuration(1, months=0, days=0, nanoseconds=0)
        >>> dd4.to_c_string()
        '0s'
    """

    return _build_c_duration_string(
        signum=self.signum,
        months=self.months,
        days=self.days,
        nanoseconds=self.nanoseconds,
    )
def to_string(self) ‑> str

Convert the DataAPIDuration to a string according to the Data API ISO-8601 standard.

Returns

a string expressing the time value in a way compatible with the Data API conventions.

Example

>>> from astrapy.data_types import DataAPIDuration
>>>
>>> dd1 = DataAPIDuration(1, months=42, days=4, nanoseconds=45005000000000)
>>> dd1.to_string()
'P3Y6M4DT12H30M5S'
>>> dd2 = DataAPIDuration(1, months=0, days=0, nanoseconds=43200000000000)
>>> dd2.to_string()
'PT12H'
Expand source code
def to_string(self) -> str:
    """
    Convert the DataAPIDuration to a string according to the Data API
    ISO-8601 standard.

    Returns:
        a string expressing the time value in a way compatible with the Data API
        conventions.

    Example:
        >>> from astrapy.data_types import DataAPIDuration
        >>>
        >>> dd1 = DataAPIDuration(1, months=42, days=4, nanoseconds=45005000000000)
        >>> dd1.to_string()
        'P3Y6M4DT12H30M5S'
        >>> dd2 = DataAPIDuration(1, months=0, days=0, nanoseconds=43200000000000)
        >>> dd2.to_string()
        'PT12H'
    """

    return _build_std_duration_string(
        signum=self.signum,
        months=self.months,
        days=self.days,
        nanoseconds=self.nanoseconds,
    )
def to_timedelta(self) ‑> datetime.timedelta

Convert a DataAPIDuration into a Python standard library datetime.timedelta.

Due to the intrinsic difference between the notions of a DataAPIDuration and the timedelta, the conversion attempt raises an error if the starting DataAPIDuration has a nonzero number of months. For the same reason, a somewhat lossy conversion occurs for a nonzero number of days since the formal notion of a day is lost in favor of that of "a span of exactly 24 hours".

Returns

a datetime.timedelta value corresponding to the origin DataAPIDuration in the best possible way.

Example

>>> from astrapy.data_types import DataAPIDuration
>>>
>>> dd1 = DataAPIDuration(
...     signum=-1, months=0, days=0, nanoseconds=93610987000000
... )
>>> dd1.to_timedelta()
datetime.timedelta(days=-2, seconds=79189, microseconds=13000)
>>> dd2 = DataAPIDuration(
...     signum=1, months=0, days=0, nanoseconds=14400000000000
... )
>>> dd2.to_timedelta()
datetime.timedelta(seconds=14400)
>>> dd3 = DataAPIDuration(signum=1, months=19, days=0, nanoseconds=0)
>>> dd3.to_timedelta()
Traceback (most recent call last):
    [...]
ValueError: Cannot convert a DataAPIDuration with nonzero months into ...
Expand source code
def to_timedelta(self) -> datetime.timedelta:
    """
    Convert a DataAPIDuration into a Python standard library `datetime.timedelta`.

    Due to the intrinsic difference between the notions of a DataAPIDuration and
    the timedelta, the conversion attempt raises an error if the starting
    DataAPIDuration has a nonzero number of months. For the same reason, a somewhat
    lossy conversion occurs for a nonzero number of days since the formal notion
    of a day is lost in favor of that of "a span of exactly 24 hours".

    Returns:
        a `datetime.timedelta` value corresponding to the origin DataAPIDuration
        in the best possible way.

    Example:
        >>> from astrapy.data_types import DataAPIDuration
        >>>
        >>> dd1 = DataAPIDuration(
        ...     signum=-1, months=0, days=0, nanoseconds=93610987000000
        ... )
        >>> dd1.to_timedelta()
        datetime.timedelta(days=-2, seconds=79189, microseconds=13000)
        >>> dd2 = DataAPIDuration(
        ...     signum=1, months=0, days=0, nanoseconds=14400000000000
        ... )
        >>> dd2.to_timedelta()
        datetime.timedelta(seconds=14400)
        >>> dd3 = DataAPIDuration(signum=1, months=19, days=0, nanoseconds=0)
        >>> dd3.to_timedelta()
        Traceback (most recent call last):
            [...]
        ValueError: Cannot convert a DataAPIDuration with nonzero months into ...
    """

    if self.months != 0:
        raise ValueError(
            "Cannot convert a DataAPIDuration with nonzero months into a timedelta."
        )
    return datetime.timedelta(
        days=self.signum * self.days,
        microseconds=self.signum * self.nanoseconds // 1000,
    )
class DataAPIMap (source: Iterable[tuple[T, U]] | dict[T, U] = [])

An immutable 'map-like' class that preserves the order and can employ non-hashable keys (which must support eq). Not designed for performance.

Despite internally preserving the order, equality between DataAPIMap instances (and with regular dicts) is independent of the order.

Expand source code
class DataAPIMap(Generic[T, U], Mapping[T, U]):
    """
    An immutable 'map-like' class that preserves the order and can employ
    non-hashable keys (which must support __eq__). Not designed for performance.

    Despite internally preserving the order, equality between DataAPIMap instances
    (and with regular dicts) is independent of the order.
    """

    _keys: list[T]
    _values: list[U]

    def __init__(self, source: Iterable[tuple[T, U]] | dict[T, U] = []) -> None:
        if isinstance(source, dict):
            self._keys, self._values = _accumulate_pairs(
                ([], []),
                source.items(),
            )
        else:
            self._keys, self._values = _accumulate_pairs(
                ([], []),
                source,
            )

    def __getitem__(self, key: T) -> U:
        if isinstance(key, float) and math.isnan(key):
            for idx, k in enumerate(self._keys):
                if isinstance(k, float) and math.isnan(k):
                    return self._values[idx]
            raise KeyError(str(key))
        else:
            for idx, k in enumerate(self._keys):
                if k == key:
                    return self._values[idx]
            raise KeyError(str(key) + "//" + str(self._keys) + "//" + str(self._values))

    def __iter__(self) -> Iterator[T]:
        return iter(self._keys)

    def __len__(self) -> int:
        return len(self._keys)

    def __eq__(self, other: object) -> bool:
        if isinstance(other, DataAPIMap):
            if len(self) == len(other):
                if all(o_k in self for o_k in other):
                    return all(other[k] == self[k] for k in self)
            return False
        try:
            dother = dict(other)  # type: ignore[call-overload]
            return all(
                [
                    len(dother) == len(self),
                    all(o_k in self for o_k in dother),
                    all(dother[k] == self[k] for k in self),
                ]
            )
        except KeyError:
            return False
        except TypeError:
            pass
        return NotImplemented

    def __repr__(self) -> str:
        _map_repr = ", ".join(
            f"({repr(k)}, {repr(v)})" for k, v in zip(self._keys, self._values)
        )
        return f"{self.__class__.__name__}([{_map_repr}])"

    def __str__(self) -> str:
        _map_repr = ", ".join(f"({k}, {v})" for k, v in zip(self._keys, self._values))
        return f"{_map_repr}"

    def __reduce__(self) -> tuple[type, tuple[Iterable[tuple[T, U]]]]:
        return self.__class__, (list(zip(self._keys, self._values)),)

Ancestors

  • collections.abc.Mapping
  • collections.abc.Collection
  • collections.abc.Sized
  • collections.abc.Iterable
  • collections.abc.Container
  • typing.Generic
class DataAPISet (items: Iterable[T] = [])

An immutable 'set-like' class that preserves the order and can store non-hashable entries (entries must support eq). Not designed for performance.

Despite internally preserving the order, equality between DataAPISet instances (and with regular sets) is independent of the order.

Expand source code
class DataAPISet(Generic[T], AbstractSet[T]):
    """
    An immutable 'set-like' class that preserves the order and can store
    non-hashable entries (entries must support __eq__). Not designed for performance.

    Despite internally preserving the order, equality between DataAPISet instances
    (and with regular sets) is independent of the order.
    """

    _items: list[T]

    def __init__(self, items: Iterable[T] = []) -> None:
        self._items = _accumulate([], items)

    def __len__(self) -> int:
        return len(self._items)

    def __getitem__(self, i: int) -> T:
        return self._items[i]

    def __iter__(self) -> Iterator[T]:
        return iter(self._items)

    def __reversed__(self) -> Iterable[T]:
        return reversed(self._items)

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}({self._items})"

    def __reduce__(self) -> tuple[type, tuple[Iterable[T]]]:
        return self.__class__, (self._items,)

    def __eq__(self, other: Any) -> bool:
        if isinstance(other, (set, DataAPISet)):
            return len(other) == len(self._items) and all(
                item in self for item in other
            )
        else:
            return NotImplemented

    def __ne__(self, other: Any) -> bool:
        if isinstance(other, self.__class__):
            return self._items != other._items
        else:
            try:
                return len(other) != len(self._items) or any(
                    item not in self for item in other
                )
            except TypeError:
                return NotImplemented

    def __le__(self, other: Any) -> bool:
        return self.issubset(other)

    def __lt__(self, other: Any) -> bool:
        return len(other) > len(self._items) and self.issubset(other)

    def __ge__(self, other: Any) -> bool:
        return self.issuperset(other)

    def __gt__(self, other: Any) -> bool:
        return len(self._items) > len(other) and self.issuperset(other)

    def __and__(self, other: Any) -> DataAPISet[T]:
        return self._intersect(other)

    __rand__ = __and__

    def __or__(self, other: Any) -> DataAPISet[T]:
        return self.union(other)

    __ror__ = __or__

    def __sub__(self, other: Any) -> DataAPISet[T]:
        return self._diff(other)

    def __rsub__(self, other: Any) -> DataAPISet[T]:
        return DataAPISet(other) - self

    def __xor__(self, other: Any) -> DataAPISet[T]:
        return self.symmetric_difference(other)

    __rxor__ = __xor__

    def __contains__(self, item: Any) -> bool:
        return item in self._items

    def isdisjoint(self, other: Any) -> bool:
        return len(self._intersect(other)) == 0

    def issubset(self, other: Any) -> bool:
        return len(self._intersect(other)) == len(self._items)

    def issuperset(self, other: Any) -> bool:
        return len(self._intersect(other)) == len(other)

    def union(self, *others: Any) -> DataAPISet[T]:
        return DataAPISet(
            _accumulate(list(iter(self)), (item for other in others for item in other)),
        )

    def intersection(self, *others: Any) -> DataAPISet[T]:
        isect = DataAPISet(iter(self))
        for other in others:
            isect = isect._intersect(other)
            if not isect:
                break
        return isect

    def difference(self, *others: Any) -> DataAPISet[T]:
        diff = DataAPISet(iter(self))
        for other in others:
            diff = diff._diff(other)
            if not diff:
                break
        return diff

    def symmetric_difference(self, other: Any) -> DataAPISet[T]:
        diff_self_other = self._diff(other)
        diff_other_self = other.difference(self)
        return diff_self_other.union(diff_other_self)

    def _diff(self, other: Any) -> DataAPISet[T]:
        return DataAPISet(
            _accumulate(
                [],
                (item for item in self._items if item not in other),
            )
        )

    def _intersect(self, other: Any) -> DataAPISet[T]:
        return DataAPISet(
            _accumulate(
                [],
                (item for item in self._items if item in other),
            )
        )

Ancestors

  • collections.abc.Set
  • collections.abc.Collection
  • collections.abc.Sized
  • collections.abc.Iterable
  • collections.abc.Container
  • typing.Generic

Methods

def difference(self, *others: Any) ‑> DataAPISet[~T]
Expand source code
def difference(self, *others: Any) -> DataAPISet[T]:
    diff = DataAPISet(iter(self))
    for other in others:
        diff = diff._diff(other)
        if not diff:
            break
    return diff
def intersection(self, *others: Any) ‑> DataAPISet[~T]
Expand source code
def intersection(self, *others: Any) -> DataAPISet[T]:
    isect = DataAPISet(iter(self))
    for other in others:
        isect = isect._intersect(other)
        if not isect:
            break
    return isect
def isdisjoint(self, other: Any) ‑> bool

Return True if two sets have a null intersection.

Expand source code
def isdisjoint(self, other: Any) -> bool:
    return len(self._intersect(other)) == 0
def issubset(self, other: Any) ‑> bool
Expand source code
def issubset(self, other: Any) -> bool:
    return len(self._intersect(other)) == len(self._items)
def issuperset(self, other: Any) ‑> bool
Expand source code
def issuperset(self, other: Any) -> bool:
    return len(self._intersect(other)) == len(other)
def symmetric_difference(self, other: Any) ‑> DataAPISet[~T]
Expand source code
def symmetric_difference(self, other: Any) -> DataAPISet[T]:
    diff_self_other = self._diff(other)
    diff_other_self = other.difference(self)
    return diff_self_other.union(diff_other_self)
def union(self, *others: Any) ‑> DataAPISet[~T]
Expand source code
def union(self, *others: Any) -> DataAPISet[T]:
    return DataAPISet(
        _accumulate(list(iter(self)), (item for other in others for item in other)),
    )
class DataAPITime (hour: int, minute: int = 0, second: int = 0, nanosecond: int = 0)

A value expressing a time, composed of hours, minutes, seconds and nanoseconds, suitable for working with the "time" table column type.

This class is designed to losslessly express the time values the Data API supports, overcoming the precision limitation of Python's standard-library time (whose sub-second quantity only has microsecond precision).

DataAPITime objects are meant to easily work in the context of the Data API, hence its conversion methods from/to a string assumed the particular format employed by the API.

The class also offers conversion methods from/to the regular Python datetime.time; however these can entail a lossy conversion because of the missing support for nanoseconds by the latter.

Args

hour
an integer in 0-23, the hour value of the time.
minute
an integer in 0-59, the minute value of the time.
second
an integer in 0-59, the second value of the time.
nanosecond
an integer in 0-999999999, the nanosecond value of the time.

Example

>>> from astrapy.data_types import DataAPITime
>>>
>>> DataAPITime(12, 34, 56)
DataAPITime(12, 34, 56, 0)
>>> t1 = DataAPITime(21, 43, 56, 789012345)
>>> t1
DataAPITime(21, 43, 56, 789012345)
>>> t1.second
56
Expand source code
@dataclass
class DataAPITime:
    """
    A value expressing a time, composed of hours, minutes, seconds and nanoseconds,
    suitable for working with the "time" table column type.

    This class is designed to losslessly express the time values the Data API
    supports, overcoming the precision limitation of Python's standard-library
    time (whose sub-second quantity only has microsecond precision).

    DataAPITime objects are meant to easily work in the context of the Data API,
    hence its conversion methods from/to a string assumed the particular format
    employed by the API.

    The class also offers conversion methods from/to the regular
    Python `datetime.time`; however these can entail a lossy conversion because
    of the missing support for nanoseconds by the latter.

    Args:
        hour: an integer in 0-23, the hour value of the time.
        minute: an integer in 0-59, the minute value of the time.
        second: an integer in 0-59, the second value of the time.
        nanosecond: an integer in 0-999999999, the nanosecond value of the time.

    Example:
        >>> from astrapy.data_types import DataAPITime
        >>>
        >>> DataAPITime(12, 34, 56)
        DataAPITime(12, 34, 56, 0)
        >>> t1 = DataAPITime(21, 43, 56, 789012345)
        >>> t1
        DataAPITime(21, 43, 56, 789012345)
        >>> t1.second
        56
    """

    hour: int
    minute: int
    second: int
    nanosecond: int

    def __init__(
        self, hour: int, minute: int = 0, second: int = 0, nanosecond: int = 0
    ):
        _fail_reason = _validate_time(
            hour=hour,
            minute=minute,
            second=second,
            nanosecond=nanosecond,
        )
        if _fail_reason:
            raise ValueError(f"Invalid time arguments: {_fail_reason}.")
        self.hour = hour
        self.minute = minute
        self.second = second
        self.nanosecond = nanosecond

    def __repr__(self) -> str:
        return (
            f"{self.__class__.__name__}({self.hour}, {self.minute}, "
            f"{self.second}, {self.nanosecond})"
        )

    def __hash__(self) -> int:
        return hash((self.hour, self.minute, self.second, self.nanosecond))

    def __str__(self) -> str:
        return self.to_string()

    def __reduce__(self) -> tuple[type, tuple[int, int, int, int]]:
        return self.__class__, (self.hour, self.minute, self.second, self.nanosecond)

    def __le__(self, other: Any) -> bool:
        if isinstance(other, DataAPITime):
            return self._to_tuple() <= other._to_tuple()
        elif isinstance(other, datetime.time):
            return self.__le__(DataAPITime.from_time(other))
        else:
            return NotImplemented

    def __lt__(self, other: Any) -> bool:
        if isinstance(other, DataAPITime):
            return self._to_tuple() < other._to_tuple()
        elif isinstance(other, datetime.time):
            return self.__lt__(DataAPITime.from_time(other))
        else:
            return NotImplemented

    def __ge__(self, other: Any) -> bool:
        if isinstance(other, DataAPITime):
            return self._to_tuple() >= other._to_tuple()
        elif isinstance(other, datetime.time):
            return self.__ge__(DataAPITime.from_time(other))
        else:
            return NotImplemented

    def __gt__(self, other: Any) -> bool:
        if isinstance(other, DataAPITime):
            return self._to_tuple() > other._to_tuple()
        elif isinstance(other, datetime.time):
            return self.__gt__(DataAPITime.from_time(other))
        else:
            return NotImplemented

    def _to_tuple(self) -> tuple[int, int, int, int]:
        return (self.hour, self.minute, self.second, self.nanosecond)

    def to_string(self) -> str:
        """
        Convert the DataAPITime to a string according to the Data API specification.

        Returns:
            a string expressing the time value in a way compatible with the Data API
            conventions.

        Example:
            >>> from astrapy.data_types import DataAPITime
            >>>
            >>> DataAPITime(12, 34, 56).to_string()
            '12:34:56'
            >>> DataAPITime(21, 43, 56, 789012345).to_string()
            '21:43:56.789012345'
        """

        hm_part = f"{self.hour:02}:{self.minute:02}"
        s_part: str
        if self.nanosecond:
            nano_div: int
            nano_digits: int
            if self.nanosecond % 1000000 == 0:
                nano_div = 1000000
                nano_digits = 3
            elif self.nanosecond % 1000 == 0:
                nano_div = 1000
                nano_digits = 6
            else:
                nano_div = 1
                nano_digits = 9
            ns_format_string = f"%0{nano_digits}i"
            s_part = (
                f"{self.second:02}.{ns_format_string % (self.nanosecond // nano_div)}"
            )
        else:
            s_part = f"{self.second:02}"
        return f"{hm_part}:{s_part}"

    def to_time(self) -> datetime.time:
        """
        Convert the DataAPITime into a Python standard-library `datetime.time` object.
        This may involve a loss of precision since the latter cannot express
        nanoseconds, only microseconds.

        Returns:
            a `datetime.time` object, corresponding (possibly in a lossy way)
            to the original DataAPITime.

        Example:
            >>> from astrapy.data_types import DataAPITime
            >>>
            >>> DataAPITime(12, 34, 56).to_time()
            datetime.time(12, 34, 56)
            >>> DataAPITime(21, 43, 56, 789012345).to_time()
            datetime.time(21, 43, 56, 789012)
        """

        return datetime.time(
            hour=self.hour,
            minute=self.minute,
            second=self.second,
            microsecond=int(self.nanosecond / 1000.0),
        )

    @staticmethod
    def from_time(dt: datetime.time) -> DataAPITime:
        """
        Create a DataAPITime from a Python standard-library `datetime.time` object.

        Args:
            dt: a `datetime.time` value

        Returns:
            a DataAPITime object, corresponding exactly to the provided input.

        Example:
            >>> from datetime import time
            >>>
            >>> from astrapy.data_types import DataAPITime
            >>>
            >>> DataAPITime.from_time(time(12, 34, 56))
            DataAPITime(12, 34, 56, 0)
            >>> DataAPITime.from_time(time(12, 34, 56, 789012))
            DataAPITime(12, 34, 56, 789012000)
        """
        return DataAPITime(
            hour=dt.hour,
            minute=dt.minute,
            second=dt.second,
            nanosecond=dt.microsecond * 1000,
        )

    @staticmethod
    def from_string(time_string: str) -> DataAPITime:
        """
        Parse a string, expressed according to the Data API format, into a DataAPITime.
        If the format is not recognized, a ValueError is raised.

        Args:
            time_string: a string compliant to the Data API specification for times.

        Returns:
            a DataAPITime corresponding to the provided input.

        Example:
            >>> from astrapy.data_types import DataAPITime
            >>>
            >>> DataAPITime.from_string("12:34:56")
            DataAPITime(12, 34, 56, 0)
            >>> DataAPITime.from_string("21:43:56.789012345")
            DataAPITime(21, 43, 56, 789012345)
            >>> DataAPITime.from_string("34:11:22.123")
            Traceback (most recent call last):
              [...]
            ValueError: Cannot parse '34:11:22.123' into a valid time: illegal hour. ...
        """

        match = TIME_PARSE_PATTERN.match(time_string)
        if match:
            hour = int(match[1])
            minute = int(match[2])
            second = int(match[3])
            nanosecond: int
            if match[4]:
                nanosecond = int(float(match[4]) * 1000000000)
            else:
                nanosecond = 0
            _fail_reason = _validate_time(
                hour=hour,
                minute=minute,
                second=second,
                nanosecond=nanosecond,
            )
            if _fail_reason:
                raise ValueError(
                    f"Cannot parse '{time_string}' into a valid time: "
                    f"{_fail_reason}. {TIME_FORMAT_DESC}"
                )
            return DataAPITime(
                hour=hour,
                minute=minute,
                second=second,
                nanosecond=nanosecond,
            )
        else:
            raise ValueError(
                f"Cannot parse '{time_string}' into a valid time "
                f"(unrecognized format). {TIME_FORMAT_DESC}"
            )

Class variables

var hour : int
var minute : int
var nanosecond : int
var second : int

Static methods

def from_string(time_string: str) ‑> DataAPITime

Parse a string, expressed according to the Data API format, into a DataAPITime. If the format is not recognized, a ValueError is raised.

Args

time_string
a string compliant to the Data API specification for times.

Returns

a DataAPITime corresponding to the provided input.

Example

>>> from astrapy.data_types import DataAPITime
>>>
>>> DataAPITime.from_string("12:34:56")
DataAPITime(12, 34, 56, 0)
>>> DataAPITime.from_string("21:43:56.789012345")
DataAPITime(21, 43, 56, 789012345)
>>> DataAPITime.from_string("34:11:22.123")
Traceback (most recent call last):
  [...]
ValueError: Cannot parse '34:11:22.123' into a valid time: illegal hour. ...
Expand source code
@staticmethod
def from_string(time_string: str) -> DataAPITime:
    """
    Parse a string, expressed according to the Data API format, into a DataAPITime.
    If the format is not recognized, a ValueError is raised.

    Args:
        time_string: a string compliant to the Data API specification for times.

    Returns:
        a DataAPITime corresponding to the provided input.

    Example:
        >>> from astrapy.data_types import DataAPITime
        >>>
        >>> DataAPITime.from_string("12:34:56")
        DataAPITime(12, 34, 56, 0)
        >>> DataAPITime.from_string("21:43:56.789012345")
        DataAPITime(21, 43, 56, 789012345)
        >>> DataAPITime.from_string("34:11:22.123")
        Traceback (most recent call last):
          [...]
        ValueError: Cannot parse '34:11:22.123' into a valid time: illegal hour. ...
    """

    match = TIME_PARSE_PATTERN.match(time_string)
    if match:
        hour = int(match[1])
        minute = int(match[2])
        second = int(match[3])
        nanosecond: int
        if match[4]:
            nanosecond = int(float(match[4]) * 1000000000)
        else:
            nanosecond = 0
        _fail_reason = _validate_time(
            hour=hour,
            minute=minute,
            second=second,
            nanosecond=nanosecond,
        )
        if _fail_reason:
            raise ValueError(
                f"Cannot parse '{time_string}' into a valid time: "
                f"{_fail_reason}. {TIME_FORMAT_DESC}"
            )
        return DataAPITime(
            hour=hour,
            minute=minute,
            second=second,
            nanosecond=nanosecond,
        )
    else:
        raise ValueError(
            f"Cannot parse '{time_string}' into a valid time "
            f"(unrecognized format). {TIME_FORMAT_DESC}"
        )
def from_time(dt: datetime.time) ‑> DataAPITime

Create a DataAPITime from a Python standard-library datetime.time object.

Args

dt
a datetime.time value

Returns

a DataAPITime object, corresponding exactly to the provided input.

Example

>>> from datetime import time
>>>
>>> from astrapy.data_types import DataAPITime
>>>
>>> DataAPITime.from_time(time(12, 34, 56))
DataAPITime(12, 34, 56, 0)
>>> DataAPITime.from_time(time(12, 34, 56, 789012))
DataAPITime(12, 34, 56, 789012000)
Expand source code
@staticmethod
def from_time(dt: datetime.time) -> DataAPITime:
    """
    Create a DataAPITime from a Python standard-library `datetime.time` object.

    Args:
        dt: a `datetime.time` value

    Returns:
        a DataAPITime object, corresponding exactly to the provided input.

    Example:
        >>> from datetime import time
        >>>
        >>> from astrapy.data_types import DataAPITime
        >>>
        >>> DataAPITime.from_time(time(12, 34, 56))
        DataAPITime(12, 34, 56, 0)
        >>> DataAPITime.from_time(time(12, 34, 56, 789012))
        DataAPITime(12, 34, 56, 789012000)
    """
    return DataAPITime(
        hour=dt.hour,
        minute=dt.minute,
        second=dt.second,
        nanosecond=dt.microsecond * 1000,
    )

Methods

def to_string(self) ‑> str

Convert the DataAPITime to a string according to the Data API specification.

Returns

a string expressing the time value in a way compatible with the Data API conventions.

Example

>>> from astrapy.data_types import DataAPITime
>>>
>>> DataAPITime(12, 34, 56).to_string()
'12:34:56'
>>> DataAPITime(21, 43, 56, 789012345).to_string()
'21:43:56.789012345'
Expand source code
def to_string(self) -> str:
    """
    Convert the DataAPITime to a string according to the Data API specification.

    Returns:
        a string expressing the time value in a way compatible with the Data API
        conventions.

    Example:
        >>> from astrapy.data_types import DataAPITime
        >>>
        >>> DataAPITime(12, 34, 56).to_string()
        '12:34:56'
        >>> DataAPITime(21, 43, 56, 789012345).to_string()
        '21:43:56.789012345'
    """

    hm_part = f"{self.hour:02}:{self.minute:02}"
    s_part: str
    if self.nanosecond:
        nano_div: int
        nano_digits: int
        if self.nanosecond % 1000000 == 0:
            nano_div = 1000000
            nano_digits = 3
        elif self.nanosecond % 1000 == 0:
            nano_div = 1000
            nano_digits = 6
        else:
            nano_div = 1
            nano_digits = 9
        ns_format_string = f"%0{nano_digits}i"
        s_part = (
            f"{self.second:02}.{ns_format_string % (self.nanosecond // nano_div)}"
        )
    else:
        s_part = f"{self.second:02}"
    return f"{hm_part}:{s_part}"
def to_time(self) ‑> datetime.time

Convert the DataAPITime into a Python standard-library datetime.time object. This may involve a loss of precision since the latter cannot express nanoseconds, only microseconds.

Returns

a datetime.time object, corresponding (possibly in a lossy way) to the original DataAPITime.

Example

>>> from astrapy.data_types import DataAPITime
>>>
>>> DataAPITime(12, 34, 56).to_time()
datetime.time(12, 34, 56)
>>> DataAPITime(21, 43, 56, 789012345).to_time()
datetime.time(21, 43, 56, 789012)
Expand source code
def to_time(self) -> datetime.time:
    """
    Convert the DataAPITime into a Python standard-library `datetime.time` object.
    This may involve a loss of precision since the latter cannot express
    nanoseconds, only microseconds.

    Returns:
        a `datetime.time` object, corresponding (possibly in a lossy way)
        to the original DataAPITime.

    Example:
        >>> from astrapy.data_types import DataAPITime
        >>>
        >>> DataAPITime(12, 34, 56).to_time()
        datetime.time(12, 34, 56)
        >>> DataAPITime(21, 43, 56, 789012345).to_time()
        datetime.time(21, 43, 56, 789012)
    """

    return datetime.time(
        hour=self.hour,
        minute=self.minute,
        second=self.second,
        microsecond=int(self.nanosecond / 1000.0),
    )
class DataAPITimestamp (timestamp_ms: int)

A value expressing a unambiguous timestamp, accurate to down millisecond precision, suitable for working with the "timestamp" table column type.

A DataAPITimestamp can be thought of as an integer signed number of milliseconds elapsed since (or before) the epoch (that is, 1970-01-01 00:00:00 GMT+0). An alternative representation is that of a "date + time + offset", such as the (year, month, day; hour, minute, second, millisecond; offset), where offset is a quantity of type "hours:minutes" essentially canceling the timezone ambiguity of the remaining terms. This latter representation thus is not a bijection to the former, as different representation map to one and the same timestamp integer value.

The fact that DataAPITimestamp values are only identified by their timestamp value is one of the key differences between this class and the Python standard-library datetime.datetime; another important practical difference is the available year range, which for the latter spans the 1AD-9999AD time period while for the DataAPITimestamp is unlimited for most practical purposes. In particular, this class is designed to losslessly express the full date range the Data API supports.

DataAPITimestamp objects are meant to easily work in the context of the Data API, hence its conversion methods from/to a string assumed the particular format employed by the API.

The class also offers conversion methods from/to the regular Python datetime.datetime; however these may fail if the year falls outside of the range supported by the latter.

Args

timestamp_ms
an integer number of milliseconds elapsed since the epoch. Negative numbers signify timestamp occurring before the epoch.

Example

>>> from astrapy.data_types import DataAPITimestamp
>>>
>>> ds1 = DataAPITimestamp(2000000000321)
>>> ds1
DataAPITimestamp(timestamp_ms=2000000000321 [2033-05-18T03:33:20.321Z])
>>> ds1.timestamp_ms
2000000000321
>>> DataAPITimestamp(-1000000000321)
DataAPITimestamp(timestamp_ms=-1000000000321 [1938-04-24T22:13:19.679Z])
Expand source code
@dataclass
class DataAPITimestamp:
    """
    A value expressing a unambiguous timestamp, accurate to down millisecond precision,
    suitable for working with the "timestamp" table column type.

    A DataAPITimestamp can be thought of as an integer signed number of milliseconds
    elapsed since (or before) the epoch (that is, 1970-01-01 00:00:00 GMT+0).
    An alternative representation is that of a "date + time + offset", such as
    the (year, month, day; hour, minute, second, millisecond; offset), where offset
    is a quantity of type "hours:minutes" essentially canceling the timezone ambiguity
    of the remaining terms. This latter representation thus is not a bijection to the
    former, as different representation map to one and the same timestamp integer value.

    The fact that DataAPITimestamp values are only identified by their timestamp value
    is one of the key differences between this class and the Python standard-library
    `datetime.datetime`; another important practical difference is the available year
    range, which for the latter spans the 1AD-9999AD time period while for the
    DataAPITimestamp is unlimited for most practical purposes. In particular,
    this class is designed to losslessly express the full date range the Data API
    supports.

    DataAPITimestamp objects are meant to easily work in the context of the Data API,
    hence its conversion methods from/to a string assumed the particular format
    employed by the API.

    The class also offers conversion methods from/to the regular
    Python `datetime.datetime`; however these may fail if the year falls outside
    of the range supported by the latter.

    Args:
        timestamp_ms: an integer number of milliseconds elapsed since the epoch.
            Negative numbers signify timestamp occurring before the epoch.

    Example:
        >>> from astrapy.data_types import DataAPITimestamp
        >>>
        >>> ds1 = DataAPITimestamp(2000000000321)
        >>> ds1
        DataAPITimestamp(timestamp_ms=2000000000321 [2033-05-18T03:33:20.321Z])
        >>> ds1.timestamp_ms
        2000000000321
        >>> DataAPITimestamp(-1000000000321)
        DataAPITimestamp(timestamp_ms=-1000000000321 [1938-04-24T22:13:19.679Z])
    """

    timestamp_ms: int

    def __repr__(self) -> str:
        return (
            f"{self.__class__.__name__}(timestamp_ms={self.timestamp_ms}"
            f" [{self.to_string()}])"
        )

    def __str__(self) -> str:
        return self.to_string()

    def __hash__(self) -> int:
        return self.timestamp_ms

    def __reduce__(self) -> tuple[type, tuple[int]]:
        return self.__class__, (self.timestamp_ms,)

    def __le__(self, other: Any) -> bool:
        if isinstance(other, DataAPITimestamp):
            return self.timestamp_ms <= other.timestamp_ms
        else:
            return NotImplemented

    def __lt__(self, other: Any) -> bool:
        if isinstance(other, DataAPITimestamp):
            return self.timestamp_ms < other.timestamp_ms
        else:
            return NotImplemented

    def __ge__(self, other: Any) -> bool:
        if isinstance(other, DataAPITimestamp):
            return self.timestamp_ms >= other.timestamp_ms
        else:
            return NotImplemented

    def __gt__(self, other: Any) -> bool:
        if isinstance(other, DataAPITimestamp):
            return self.timestamp_ms > other.timestamp_ms
        else:
            return NotImplemented

    def __sub__(self, other: Any) -> int:
        if isinstance(other, DataAPITimestamp):
            return self.timestamp_ms - other.timestamp_ms
        else:
            return NotImplemented

    def to_datetime(self, *, tz: datetime.timezone | None) -> datetime.datetime:
        """
        Convert the DataAPITimestamp into a standard-library `datetime.datetime` value.

        The conversion may fail if the target year range is outside of the capabilities
        of `datetime.datetime`, in which case a ValueError will be raised.

        Args:
            tz: a `datetime.timezone` setting for providing offset information to the
                result, thus making it an "aware" datetime. If a "naive" datetime is
                desired (which however may lead to inconsistent timestamp handling
                throughout the application), it is possible to pass `tz=None`.

        Returns:
            A `datetime.datetime` value, set to the desired timezone - or naive, in case
            a null timezone is explicitly provided.

        Example:
            >>> import datetime
            >>>
            >>> from astrapy.data_types import DataAPITimestamp
            >>>
            >>> ds1 = DataAPITimestamp(2000000000321)
            >>> ds1.to_datetime(tz=datetime.timezone.utc)
            datetime.datetime(2033, 5, 18, 3, 33, 20, 321000, tzinfo=datetime.timezone.utc)
            >>> ds1.to_datetime(tz=None)
            datetime.datetime(2033, 5, 18, 5, 33, 20, 321000)
            >>>
            >>> ds2 = DataAPITimestamp(300000000000000)
            >>> ds2.to_datetime(tz=datetime.timezone.utc)
            Traceback (most recent call last):
              [...]
            ValueError: year 11476 is out of range
        """

        return datetime.datetime.fromtimestamp(self.timestamp_ms / 1000.0, tz=tz)

    def to_naive_datetime(self) -> datetime.datetime:
        """
        Convert the DataAPITimestamp into a standard-library  naive
        `datetime.datetime` value, i.e. one without attached timezone/offset info.

        The conversion may fail if the target year range is outside of the capabilities
        of `datetime.datetime`, in which case a ValueError will be raised.

        Returns:
            A naive `datetime.datetime` value. The ambiguity stemming from the lack of
            timezone information is handed off to the standard-library `.fromtimestamp`
            method, which works in the timezone set by the system locale.

        Example:
            >>> import datetime
            >>>
            >>> from astrapy.data_types import DataAPITimestamp
            >>>
            >>> ds1 = DataAPITimestamp(300000000321)
            >>> ds1
            DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
            >>> # running in wintertime, in the Paris/Berlin timezone:
            >>> ds1.to_naive_datetime()
            datetime.datetime(1979, 7, 5, 7, 20, 0, 321000)
        """

        return datetime.datetime.fromtimestamp(self.timestamp_ms / 1000.0, tz=None)

    @staticmethod
    def from_datetime(dt: datetime.datetime) -> DataAPITimestamp:
        """
        Convert a standard-library `datetime.datetime` into a DataAPITimestamp.

        The conversion correctly takes timezone information into account, if provided.
        If it is absent (naive datetime), the ambiguity is resolved by the stdlib
        `.timestamp()` method, which assumes the timezone set by the system locale.

        Args:
            dt: the `datetime.datetime` to convert into a DataAPITimestamp.

        Returns:
            A DataAPITimestamp, corresponding to the provided datetime.

        Example (running in the Paris/Berlin timezone):
            >>> import datetime
            >>>
            >>> from astrapy.data_types import DataAPITimestamp
            >>>
            >>> ds1 = DataAPITimestamp(300000000321)
            >>> ds1
            DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
            >>> ds1.to_naive_datetime()
            datetime.datetime(1979, 7, 5, 7, 20, 0, 321000)
        """

        return DataAPITimestamp(timestamp_ms=int(dt.timestamp() * 1000.0))

    @staticmethod
    def from_string(datetime_string: str) -> DataAPITimestamp:
        """
        Convert a string into a DataAPITimestamp, provided the string represents one
        according to the Data API RFC3339 format conventions. If the format is
        unrecognized, a ValueError is raised.

        Args:
            date_string: a string expressing a timestamp as per Data API conventions.

        Returns:
            a DataAPITimestamp corresponding to the provided input.

        Example:
            >>> from astrapy.data_types import DataAPITimestamp
            >>>
            >>> DataAPITimestamp.from_string("2021-07-18T14:56:23.987Z")
            DataAPITimestamp(timestamp_ms=1626620183987 [2021-07-18T14:56:23.987Z])
            >>> DataAPITimestamp.from_string("-0044-03-15T11:22:33+01:00")
            DataAPITimestamp(timestamp_ms=-63549322647000 [-0044-03-15T10:22:33.000Z])
            >>> # missing trailing offset information:
            >>> DataAPITimestamp.from_string("1991-11-22T01:23:45.678")
            Traceback (most recent call last):
              [...]
            ValueError: Cannot parse '1991-11-22T01:23:45.678' into a valid timestamp...
        """
        _datetime_string = datetime_string.upper().replace("Z", "+00:00")
        match = TIMESTAMP_PARSE_PATTERN.match(_datetime_string)
        if match:
            # the year string has additional constraints besides the regexp:
            year_str = match[1]
            if year_str and year_str[0] == "+":
                if len(year_str[1:]) <= 4:
                    raise ValueError(
                        f"Cannot parse '{datetime_string}' into a valid timestamp: "
                        "four-digit positive year should bear no plus sign. "
                        f"{TIMESTAMP_FORMAT_DESC}"
                    )
            if len(year_str) > 4 and year_str[0] not in {"+", "-"}:
                raise ValueError(
                    f"Cannot parse '{datetime_string}' into a valid timestamp: "
                    "years with more than four digits should bear a leading sign. "
                    f"{TIMESTAMP_FORMAT_DESC}"
                )
            year = int(year_str)
            if year == 0 and year_str[0] == "-":
                raise ValueError(
                    f"Cannot parse '{datetime_string}' into a valid timestamp: "
                    "year zero should be provided as '0000' without leading sign. "
                    f"{TIMESTAMP_FORMAT_DESC}"
                )
            month = int(match[2])
            day = int(match[3])
            hour = int(match[4])
            minute = int(match[5])
            second = int(match[6])
            millisecond: int
            if match[7]:
                millisecond = int(float(match[7]) * 1000)
            else:
                millisecond = 0
            offset_hour = int(match[8])
            offset_minute = int(match[9])

            # validations
            _d_f_reason = _validate_date(
                year=year,
                month=month,
                day=day,
            )
            if _d_f_reason:
                raise ValueError(
                    f"Cannot parse '{datetime_string}' into a valid timestamp: "
                    f"{_d_f_reason}. {TIMESTAMP_FORMAT_DESC}"
                )
            _t_f_reason = _validate_time(
                hour=hour,
                minute=minute,
                second=second,
                nanosecond=millisecond * 1000000,
            )
            if _t_f_reason:
                raise ValueError(
                    f"Cannot parse '{datetime_string}' into a valid timestamp: "
                    f"{_t_f_reason}. {TIMESTAMP_FORMAT_DESC}"
                )
            # validate offset
            if offset_hour < -23 or offset_hour > 23:
                raise ValueError(
                    f"Cannot parse '{datetime_string}' into a valid timestamp: "
                    f"illegal offset hours. {TIMESTAMP_FORMAT_DESC}"
                )
            if offset_minute < 0 or offset_hour > 59:
                raise ValueError(
                    f"Cannot parse '{datetime_string}' into a valid timestamp: "
                    f"illegal offset minutes. {TIMESTAMP_FORMAT_DESC}"
                )

            # convert into a timestamp, part 1: year
            year_timestamp_ms = _year_to_unix_timestamp_ms(year)

            # convert into a timestamp, part 2: the rest (taking care of offset as well)
            ref_year = 1972 if _is_leap_year(year) else 1971
            year_start_date = datetime.datetime(ref_year, 1, 1, 0, 0, 0, 0).replace(
                tzinfo=datetime.timezone.utc
            )
            offset_delta = datetime.timedelta(hours=offset_hour, minutes=offset_minute)
            year_reset_date = datetime.datetime(
                ref_year, month, day, hour, minute, second, millisecond * 1000
            ).replace(tzinfo=datetime.timezone.utc)
            in_year_timestamp_ms = int(
                (year_reset_date - offset_delta - year_start_date).total_seconds()
                * 1000
            )

            return DataAPITimestamp(
                timestamp_ms=year_timestamp_ms + in_year_timestamp_ms
            )
        else:
            raise ValueError(
                f"Cannot parse '{datetime_string}' into a valid timestamp "
                f"(unrecognized format). {TIMESTAMP_FORMAT_DESC}"
            )

    def timetuple(self) -> tuple[int, int, int, int, int, int, int]:
        """
        Convert the DataAPITimestamp into a 7-item tuple expressing
        the corresponding datetime in UTC, i.e. with implied "+00:00" offset.

        Returns:
            a (year, month, day, hour, minute, second, millisecond) of integers.
            Note the last entry is millisecond.

        Example:
            >>> from astrapy.data_types import DataAPITimestamp
            >>>
            >>> dt1 = DataAPITimestamp(300000000321)
            >>> dt1
            DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
            >>> dt1.timetuple()
            (1979, 7, 5, 5, 20, 0, 321)
        """
        return _unix_timestamp_ms_to_timetuple(self.timestamp_ms)

    def to_string(self) -> str:
        """
        Express the timestamp as a string according to the Data API RFC3339 syntax,
        including the presence of a sign, and the number of digits, for the year.

        The string returned from this method can be directly used in the appropriate
        parts of a payload to the Data API.

        Note that there is no parameter to control formatting (as there would be
        for `datetime.strftime`). To customize the formatting, one should invoke
        `DataAPITimestamp.timetuple` and use its output subsequently.

        Returns:
            a string, such as "2024-12-31T12:34:56.543Z", formatted in a way suitable
            to be used in a Data API payload.

        Example:
            >>> from astrapy.data_types import DataAPITimestamp
            >>>
            >>> dt1 = DataAPITimestamp(300000000321)
            >>> dt1
            DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
            >>> dt1.to_string()
            '1979-07-05T05:20:00.321Z'
        """

        y, mo, d, h, m, s, ms = self.timetuple()
        # the year part requires care around the sign and number of digits
        year_str: str
        if y > 9999:
            year_str = f"{y:+}"
        elif y >= 0:
            year_str = f"{y:04}"
        else:
            year_str = f"{y:+05}"
        return f"{year_str}-{mo:02}-{d:02}T{h:02}:{m:02}:{s:02}.{ms:03}Z"

Class variables

var timestamp_ms : int

Static methods

def from_datetime(dt: datetime.datetime) ‑> DataAPITimestamp

Convert a standard-library datetime.datetime into a DataAPITimestamp.

The conversion correctly takes timezone information into account, if provided. If it is absent (naive datetime), the ambiguity is resolved by the stdlib .timestamp() method, which assumes the timezone set by the system locale.

Args

dt
the datetime.datetime to convert into a DataAPITimestamp.

Returns

A DataAPITimestamp, corresponding to the provided datetime. Example (running in the Paris/Berlin timezone): >>> import datetime >>> >>> from astrapy.data_types import DataAPITimestamp >>> >>> ds1 = DataAPITimestamp(300000000321) >>> ds1 DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z]) >>> ds1.to_naive_datetime() datetime.datetime(1979, 7, 5, 7, 20, 0, 321000)

Expand source code
@staticmethod
def from_datetime(dt: datetime.datetime) -> DataAPITimestamp:
    """
    Convert a standard-library `datetime.datetime` into a DataAPITimestamp.

    The conversion correctly takes timezone information into account, if provided.
    If it is absent (naive datetime), the ambiguity is resolved by the stdlib
    `.timestamp()` method, which assumes the timezone set by the system locale.

    Args:
        dt: the `datetime.datetime` to convert into a DataAPITimestamp.

    Returns:
        A DataAPITimestamp, corresponding to the provided datetime.

    Example (running in the Paris/Berlin timezone):
        >>> import datetime
        >>>
        >>> from astrapy.data_types import DataAPITimestamp
        >>>
        >>> ds1 = DataAPITimestamp(300000000321)
        >>> ds1
        DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
        >>> ds1.to_naive_datetime()
        datetime.datetime(1979, 7, 5, 7, 20, 0, 321000)
    """

    return DataAPITimestamp(timestamp_ms=int(dt.timestamp() * 1000.0))
def from_string(datetime_string: str) ‑> DataAPITimestamp

Convert a string into a DataAPITimestamp, provided the string represents one according to the Data API RFC3339 format conventions. If the format is unrecognized, a ValueError is raised.

Args

date_string
a string expressing a timestamp as per Data API conventions.

Returns

a DataAPITimestamp corresponding to the provided input.

Example

>>> from astrapy.data_types import DataAPITimestamp
>>>
>>> DataAPITimestamp.from_string("2021-07-18T14:56:23.987Z")
DataAPITimestamp(timestamp_ms=1626620183987 [2021-07-18T14:56:23.987Z])
>>> DataAPITimestamp.from_string("-0044-03-15T11:22:33+01:00")
DataAPITimestamp(timestamp_ms=-63549322647000 [-0044-03-15T10:22:33.000Z])
>>> # missing trailing offset information:
>>> DataAPITimestamp.from_string("1991-11-22T01:23:45.678")
Traceback (most recent call last):
  [...]
ValueError: Cannot parse '1991-11-22T01:23:45.678' into a valid timestamp...
Expand source code
@staticmethod
def from_string(datetime_string: str) -> DataAPITimestamp:
    """
    Convert a string into a DataAPITimestamp, provided the string represents one
    according to the Data API RFC3339 format conventions. If the format is
    unrecognized, a ValueError is raised.

    Args:
        date_string: a string expressing a timestamp as per Data API conventions.

    Returns:
        a DataAPITimestamp corresponding to the provided input.

    Example:
        >>> from astrapy.data_types import DataAPITimestamp
        >>>
        >>> DataAPITimestamp.from_string("2021-07-18T14:56:23.987Z")
        DataAPITimestamp(timestamp_ms=1626620183987 [2021-07-18T14:56:23.987Z])
        >>> DataAPITimestamp.from_string("-0044-03-15T11:22:33+01:00")
        DataAPITimestamp(timestamp_ms=-63549322647000 [-0044-03-15T10:22:33.000Z])
        >>> # missing trailing offset information:
        >>> DataAPITimestamp.from_string("1991-11-22T01:23:45.678")
        Traceback (most recent call last):
          [...]
        ValueError: Cannot parse '1991-11-22T01:23:45.678' into a valid timestamp...
    """
    _datetime_string = datetime_string.upper().replace("Z", "+00:00")
    match = TIMESTAMP_PARSE_PATTERN.match(_datetime_string)
    if match:
        # the year string has additional constraints besides the regexp:
        year_str = match[1]
        if year_str and year_str[0] == "+":
            if len(year_str[1:]) <= 4:
                raise ValueError(
                    f"Cannot parse '{datetime_string}' into a valid timestamp: "
                    "four-digit positive year should bear no plus sign. "
                    f"{TIMESTAMP_FORMAT_DESC}"
                )
        if len(year_str) > 4 and year_str[0] not in {"+", "-"}:
            raise ValueError(
                f"Cannot parse '{datetime_string}' into a valid timestamp: "
                "years with more than four digits should bear a leading sign. "
                f"{TIMESTAMP_FORMAT_DESC}"
            )
        year = int(year_str)
        if year == 0 and year_str[0] == "-":
            raise ValueError(
                f"Cannot parse '{datetime_string}' into a valid timestamp: "
                "year zero should be provided as '0000' without leading sign. "
                f"{TIMESTAMP_FORMAT_DESC}"
            )
        month = int(match[2])
        day = int(match[3])
        hour = int(match[4])
        minute = int(match[5])
        second = int(match[6])
        millisecond: int
        if match[7]:
            millisecond = int(float(match[7]) * 1000)
        else:
            millisecond = 0
        offset_hour = int(match[8])
        offset_minute = int(match[9])

        # validations
        _d_f_reason = _validate_date(
            year=year,
            month=month,
            day=day,
        )
        if _d_f_reason:
            raise ValueError(
                f"Cannot parse '{datetime_string}' into a valid timestamp: "
                f"{_d_f_reason}. {TIMESTAMP_FORMAT_DESC}"
            )
        _t_f_reason = _validate_time(
            hour=hour,
            minute=minute,
            second=second,
            nanosecond=millisecond * 1000000,
        )
        if _t_f_reason:
            raise ValueError(
                f"Cannot parse '{datetime_string}' into a valid timestamp: "
                f"{_t_f_reason}. {TIMESTAMP_FORMAT_DESC}"
            )
        # validate offset
        if offset_hour < -23 or offset_hour > 23:
            raise ValueError(
                f"Cannot parse '{datetime_string}' into a valid timestamp: "
                f"illegal offset hours. {TIMESTAMP_FORMAT_DESC}"
            )
        if offset_minute < 0 or offset_hour > 59:
            raise ValueError(
                f"Cannot parse '{datetime_string}' into a valid timestamp: "
                f"illegal offset minutes. {TIMESTAMP_FORMAT_DESC}"
            )

        # convert into a timestamp, part 1: year
        year_timestamp_ms = _year_to_unix_timestamp_ms(year)

        # convert into a timestamp, part 2: the rest (taking care of offset as well)
        ref_year = 1972 if _is_leap_year(year) else 1971
        year_start_date = datetime.datetime(ref_year, 1, 1, 0, 0, 0, 0).replace(
            tzinfo=datetime.timezone.utc
        )
        offset_delta = datetime.timedelta(hours=offset_hour, minutes=offset_minute)
        year_reset_date = datetime.datetime(
            ref_year, month, day, hour, minute, second, millisecond * 1000
        ).replace(tzinfo=datetime.timezone.utc)
        in_year_timestamp_ms = int(
            (year_reset_date - offset_delta - year_start_date).total_seconds()
            * 1000
        )

        return DataAPITimestamp(
            timestamp_ms=year_timestamp_ms + in_year_timestamp_ms
        )
    else:
        raise ValueError(
            f"Cannot parse '{datetime_string}' into a valid timestamp "
            f"(unrecognized format). {TIMESTAMP_FORMAT_DESC}"
        )

Methods

def timetuple(self) ‑> tuple[int, int, int, int, int, int, int]

Convert the DataAPITimestamp into a 7-item tuple expressing the corresponding datetime in UTC, i.e. with implied "+00:00" offset.

Returns

a (year, month, day, hour, minute, second, millisecond) of integers. Note the last entry is millisecond.

Example

>>> from astrapy.data_types import DataAPITimestamp
>>>
>>> dt1 = DataAPITimestamp(300000000321)
>>> dt1
DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
>>> dt1.timetuple()
(1979, 7, 5, 5, 20, 0, 321)
Expand source code
def timetuple(self) -> tuple[int, int, int, int, int, int, int]:
    """
    Convert the DataAPITimestamp into a 7-item tuple expressing
    the corresponding datetime in UTC, i.e. with implied "+00:00" offset.

    Returns:
        a (year, month, day, hour, minute, second, millisecond) of integers.
        Note the last entry is millisecond.

    Example:
        >>> from astrapy.data_types import DataAPITimestamp
        >>>
        >>> dt1 = DataAPITimestamp(300000000321)
        >>> dt1
        DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
        >>> dt1.timetuple()
        (1979, 7, 5, 5, 20, 0, 321)
    """
    return _unix_timestamp_ms_to_timetuple(self.timestamp_ms)
def to_datetime(self, *, tz: datetime.timezone | None) ‑> datetime.datetime

Convert the DataAPITimestamp into a standard-library datetime.datetime value.

The conversion may fail if the target year range is outside of the capabilities of datetime.datetime, in which case a ValueError will be raised.

Args

tz
a datetime.timezone setting for providing offset information to the result, thus making it an "aware" datetime. If a "naive" datetime is desired (which however may lead to inconsistent timestamp handling throughout the application), it is possible to pass tz=None.

Returns

A datetime.datetime value, set to the desired timezone - or naive, in case a null timezone is explicitly provided.

Example

>>> import datetime
>>>
>>> from astrapy.data_types import DataAPITimestamp
>>>
>>> ds1 = DataAPITimestamp(2000000000321)
>>> ds1.to_datetime(tz=datetime.timezone.utc)
datetime.datetime(2033, 5, 18, 3, 33, 20, 321000, tzinfo=datetime.timezone.utc)
>>> ds1.to_datetime(tz=None)
datetime.datetime(2033, 5, 18, 5, 33, 20, 321000)
>>>
>>> ds2 = DataAPITimestamp(300000000000000)
>>> ds2.to_datetime(tz=datetime.timezone.utc)
Traceback (most recent call last):
  [...]
ValueError: year 11476 is out of range
Expand source code
def to_datetime(self, *, tz: datetime.timezone | None) -> datetime.datetime:
    """
    Convert the DataAPITimestamp into a standard-library `datetime.datetime` value.

    The conversion may fail if the target year range is outside of the capabilities
    of `datetime.datetime`, in which case a ValueError will be raised.

    Args:
        tz: a `datetime.timezone` setting for providing offset information to the
            result, thus making it an "aware" datetime. If a "naive" datetime is
            desired (which however may lead to inconsistent timestamp handling
            throughout the application), it is possible to pass `tz=None`.

    Returns:
        A `datetime.datetime` value, set to the desired timezone - or naive, in case
        a null timezone is explicitly provided.

    Example:
        >>> import datetime
        >>>
        >>> from astrapy.data_types import DataAPITimestamp
        >>>
        >>> ds1 = DataAPITimestamp(2000000000321)
        >>> ds1.to_datetime(tz=datetime.timezone.utc)
        datetime.datetime(2033, 5, 18, 3, 33, 20, 321000, tzinfo=datetime.timezone.utc)
        >>> ds1.to_datetime(tz=None)
        datetime.datetime(2033, 5, 18, 5, 33, 20, 321000)
        >>>
        >>> ds2 = DataAPITimestamp(300000000000000)
        >>> ds2.to_datetime(tz=datetime.timezone.utc)
        Traceback (most recent call last):
          [...]
        ValueError: year 11476 is out of range
    """

    return datetime.datetime.fromtimestamp(self.timestamp_ms / 1000.0, tz=tz)
def to_naive_datetime(self) ‑> datetime.datetime

Convert the DataAPITimestamp into a standard-library naive datetime.datetime value, i.e. one without attached timezone/offset info.

The conversion may fail if the target year range is outside of the capabilities of datetime.datetime, in which case a ValueError will be raised.

Returns

A naive datetime.datetime value. The ambiguity stemming from the lack of timezone information is handed off to the standard-library .fromtimestamp method, which works in the timezone set by the system locale.

Example

>>> import datetime
>>>
>>> from astrapy.data_types import DataAPITimestamp
>>>
>>> ds1 = DataAPITimestamp(300000000321)
>>> ds1
DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
>>> # running in wintertime, in the Paris/Berlin timezone:
>>> ds1.to_naive_datetime()
datetime.datetime(1979, 7, 5, 7, 20, 0, 321000)
Expand source code
def to_naive_datetime(self) -> datetime.datetime:
    """
    Convert the DataAPITimestamp into a standard-library  naive
    `datetime.datetime` value, i.e. one without attached timezone/offset info.

    The conversion may fail if the target year range is outside of the capabilities
    of `datetime.datetime`, in which case a ValueError will be raised.

    Returns:
        A naive `datetime.datetime` value. The ambiguity stemming from the lack of
        timezone information is handed off to the standard-library `.fromtimestamp`
        method, which works in the timezone set by the system locale.

    Example:
        >>> import datetime
        >>>
        >>> from astrapy.data_types import DataAPITimestamp
        >>>
        >>> ds1 = DataAPITimestamp(300000000321)
        >>> ds1
        DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
        >>> # running in wintertime, in the Paris/Berlin timezone:
        >>> ds1.to_naive_datetime()
        datetime.datetime(1979, 7, 5, 7, 20, 0, 321000)
    """

    return datetime.datetime.fromtimestamp(self.timestamp_ms / 1000.0, tz=None)
def to_string(self) ‑> str

Express the timestamp as a string according to the Data API RFC3339 syntax, including the presence of a sign, and the number of digits, for the year.

The string returned from this method can be directly used in the appropriate parts of a payload to the Data API.

Note that there is no parameter to control formatting (as there would be for datetime.strftime). To customize the formatting, one should invoke DataAPITimestamp.timetuple() and use its output subsequently.

Returns

a string, such as "2024-12-31T12:34:56.543Z", formatted in a way suitable to be used in a Data API payload.

Example

>>> from astrapy.data_types import DataAPITimestamp
>>>
>>> dt1 = DataAPITimestamp(300000000321)
>>> dt1
DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
>>> dt1.to_string()
'1979-07-05T05:20:00.321Z'
Expand source code
def to_string(self) -> str:
    """
    Express the timestamp as a string according to the Data API RFC3339 syntax,
    including the presence of a sign, and the number of digits, for the year.

    The string returned from this method can be directly used in the appropriate
    parts of a payload to the Data API.

    Note that there is no parameter to control formatting (as there would be
    for `datetime.strftime`). To customize the formatting, one should invoke
    `DataAPITimestamp.timetuple` and use its output subsequently.

    Returns:
        a string, such as "2024-12-31T12:34:56.543Z", formatted in a way suitable
        to be used in a Data API payload.

    Example:
        >>> from astrapy.data_types import DataAPITimestamp
        >>>
        >>> dt1 = DataAPITimestamp(300000000321)
        >>> dt1
        DataAPITimestamp(timestamp_ms=300000000321 [1979-07-05T05:20:00.321Z])
        >>> dt1.to_string()
        '1979-07-05T05:20:00.321Z'
    """

    y, mo, d, h, m, s, ms = self.timetuple()
    # the year part requires care around the sign and number of digits
    year_str: str
    if y > 9999:
        year_str = f"{y:+}"
    elif y >= 0:
        year_str = f"{y:04}"
    else:
        year_str = f"{y:+05}"
    return f"{year_str}-{mo:02}-{d:02}T{h:02}:{m:02}:{s:02}.{ms:03}Z"
class DataAPIVector (vector: list[float] = [])

A class wrapping a list of float numbers to be treated as a "vector" within the Data API. This class has the same functionalities as the underlying list[float], plus it can be used to signal the Data API that a certain list of numbers can be encoded as a binary object (which improves on the performance and bandwidth of the write operations to the Data API).

Attributes

data
a list of float numbers, the underlying content of the vector
n
the number of components, i.e. the length of the list.

Example

>>> from astrapy.data_types import DataAPIVector
>>>
>>> v1 = DataAPIVector([0.1, -0.2, 0.3])
>>> print(v1.to_bytes())
b'=\xcc\xcc\xcd\xbeL\xcc\xcd>\x99\x99\x9a'
>>> DataAPIVector.from_bytes(b"=\xcc\xcc\xcd\xbeL\xcc\xcd>\x99\x99\x9a")
DataAPIVector([0.10000000149011612, -0.20000000298023224, 0.30000001192092896])
>>> for i, x in enumerate(v1):
...     print(f"component {i} => {x}")
...
component 0 => 0.1
component 1 => -0.2
component 2 => 0.3
Expand source code
@dataclass
class DataAPIVector(FloatList):
    r"""
    A class wrapping a list of float numbers to be treated as a "vector" within the
    Data API. This class has the same functionalities as the underlying `list[float]`,
    plus it can be used to signal the Data API that a certain list of numbers can
    be encoded as a binary object (which improves on the performance and bandwidth of
    the write operations to the Data API).

    Attributes:
        data: a list of float numbers, the underlying content of the vector
        n: the number of components, i.e. the length of the list.

    Example:
        >>> from astrapy.data_types import DataAPIVector
        >>>
        >>> v1 = DataAPIVector([0.1, -0.2, 0.3])
        >>> print(v1.to_bytes())
        b'=\xcc\xcc\xcd\xbeL\xcc\xcd>\x99\x99\x9a'
        >>> DataAPIVector.from_bytes(b"=\xcc\xcc\xcd\xbeL\xcc\xcd>\x99\x99\x9a")
        DataAPIVector([0.10000000149011612, -0.20000000298023224, 0.30000001192092896])
        >>> for i, x in enumerate(v1):
        ...     print(f"component {i} => {x}")
        ...
        component 0 => 0.1
        component 1 => -0.2
        component 2 => 0.3
    """

    data: list[float]
    n: int

    def __init__(self, vector: list[float] = []) -> None:
        self.data = vector
        self.n = len(self.data)

    def __iter__(self) -> Iterator[float]:
        return iter(self.data)

    def __hash__(self) -> int:
        return hash(tuple(self.data))

    def __repr__(self) -> str:
        if self.n < 5:
            return f"{self.__class__.__name__}({self.data})"
        else:
            data_start = f"[{', '.join(str(x) for x in self.data[:3])} ...]"
            return f"{self.__class__.__name__}({data_start}, n={self.n})"

    def __str__(self) -> str:
        if self.n < 5:
            return str(self.data)
        else:
            return f"[{', '.join(str(x) for x in self.data[:3])} ...]"

    def to_bytes(self) -> bytes:
        """
        Convert the vector into its binary blob (`bytes`) representation, according
        to the Data API convention (including endianness).

        Returns:
            a `bytes` object, expressing the vector values in a lossless way.
        """

        return floats_to_bytes(self.data, self.n)

    @staticmethod
    def from_bytes(byte_blob: bytes) -> DataAPIVector:
        """
        Create a DataAPIVector from a binary blob, decoding its contents according
        to the Data API convention (including endianness).

        Args:
            byte_blob: a binary sequence, encoding a vector of floats as specified
            by the Data API convention.

        Returns:
            a DataAPIVector corresponding to the provided blob.
        """

        return DataAPIVector(bytes_to_floats(byte_blob))

Ancestors

  • collections.UserList
  • collections.abc.MutableSequence
  • collections.abc.Sequence
  • collections.abc.Reversible
  • collections.abc.Collection
  • collections.abc.Sized
  • collections.abc.Iterable
  • collections.abc.Container

Class variables

var data : list[float]
var n : int

Static methods

def from_bytes(byte_blob: bytes) ‑> DataAPIVector

Create a DataAPIVector from a binary blob, decoding its contents according to the Data API convention (including endianness).

Args

byte_blob
a binary sequence, encoding a vector of floats as specified

by the Data API convention.

Returns

a DataAPIVector corresponding to the provided blob.

Expand source code
@staticmethod
def from_bytes(byte_blob: bytes) -> DataAPIVector:
    """
    Create a DataAPIVector from a binary blob, decoding its contents according
    to the Data API convention (including endianness).

    Args:
        byte_blob: a binary sequence, encoding a vector of floats as specified
        by the Data API convention.

    Returns:
        a DataAPIVector corresponding to the provided blob.
    """

    return DataAPIVector(bytes_to_floats(byte_blob))

Methods

def to_bytes(self) ‑> bytes

Convert the vector into its binary blob (bytes) representation, according to the Data API convention (including endianness).

Returns

a bytes object, expressing the vector values in a lossless way.

Expand source code
def to_bytes(self) -> bytes:
    """
    Convert the vector into its binary blob (`bytes`) representation, according
    to the Data API convention (including endianness).

    Returns:
        a `bytes` object, expressing the vector values in a lossless way.
    """

    return floats_to_bytes(self.data, self.n)