Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CxoTimeDescr descriptor and CxoTime.NOW sentinel #43

Merged
merged 4 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cxotime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from astropy import units
from astropy.time import TimeDelta

from .convert import *
from .cxotime import CxoTime, CxoTimeLike
from .convert import * # noqa: F401, F403
from .cxotime import * # noqa: F401, F403

__version__ = ska_helpers.get_version(__package__)

Expand Down
41 changes: 41 additions & 0 deletions cxotime/cxotime.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
import numpy.typing as npt
from astropy.time import Time, TimeCxcSec, TimeDecimalYear, TimeJD, TimeYearDayTime
from astropy.utils import iers
from ska_helpers.utils import TypedDescriptor

__all__ = ["CxoTime", "CxoTimeLike", "CxoTimeDescriptor"]


# TODO: use npt.NDArray with numpy 1.21
CxoTimeLike = Union["CxoTime", str, float, int, np.ndarray, npt.ArrayLike, None]
Expand Down Expand Up @@ -498,3 +502,40 @@ def to_value(self, parent=None, **kwargs):
return out

value = property(to_value)


class CxoTimeDescriptor(TypedDescriptor):
"""Descriptor for an attribute that is CxoTime (in date format) or None if not set.

This allows setting the attribute with any ``CxoTimeLike`` value.

Note that setting this descriptor to ``None`` will set the attribute to ``None``,
which is different than ``CxoTime(None)`` which returns the current time. To set
an attribute to the current time, set it with ``CxoTime.now()``.

jeanconn marked this conversation as resolved.
Show resolved Hide resolved
Parameters
----------
default : CxoTimeLike, optional
Default value for the attribute which is provide to the ``CxoTime`` constructor.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: provided

If not specified or ``None``, the default for the attribute is ``None``.
required : bool, optional
If ``True``, the attribute is required to be set explicitly when the object
is created. If ``False`` the default value is used if the attribute is not set.

Examples
--------
>>> from dataclasses import dataclass
>>> from cxotime import CxoTime, CxoTimeDescriptor
>>> @dataclass
... class MyClass:
... start: CxoTime | None = CxoTimeDescriptor()
... stop: CxoTime | None = CxoTimeDescriptor()
...
>>> obj = MyClass("2023:100")
>>> obj.start
<CxoTime object: scale='utc' format='date' value=2023:100:00:00:00.000>
>>> obj.stop is None
True
"""

cls = CxoTime
64 changes: 64 additions & 0 deletions cxotime/tests/test_cxotime.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""
import io
import time
from dataclasses import dataclass

import astropy.units as u
import numpy as np
Expand All @@ -17,6 +18,7 @@
# Test that cxotime.__init__ imports the CxoTime class and all converters like date2secs
from cxotime import ( # noqa: F401
CxoTime,
CxoTimeDescriptor,
convert_time_format,
date2greta,
date2jd,
Expand Down Expand Up @@ -454,3 +456,65 @@ def test_convert_time_format_obj():
"""Explicit test of convert_time_format for CxoTime object"""
tm = CxoTime(100.0)
assert tm.date == convert_time_format(tm, "date")


def test_cxotime_descriptor_not_required_no_default():
@dataclass
class MyClass:
time: CxoTime | None = CxoTimeDescriptor()

obj = MyClass()
assert obj.time is None

obj = MyClass(time="2020:001")
assert isinstance(obj.time, CxoTime)
assert obj.time.value == "2020:001:00:00:00.000"
assert obj.time.format == "date"

tm = CxoTime(100.0)
assert tm.format == "secs"

# Initialize with CxoTime object
obj = MyClass(time=tm)
assert isinstance(obj.time, CxoTime)
assert obj.time.value == 100.0

# CxoTime does not copy an existing CxoTime object for speed
assert obj.time is tm


def test_cxotime_descriptor_is_required():
@dataclass
class MyClass:
time: CxoTime = CxoTimeDescriptor(required=True)

obj = MyClass(time="2020-01-01")
assert obj.time.date == "2020:001:00:00:00.000"

with pytest.raises(
ValueError,
match="cannot set required attribute 'time' to None",
):
MyClass()


def test_cxotime_descriptor_has_default():
@dataclass
class MyClass:
time: CxoTime = CxoTimeDescriptor(default="2020-01-01")

obj = MyClass()
assert obj.time.value == "2020-01-01 00:00:00.000"

obj = MyClass(time="2023:100")
assert obj.time.value == "2023:100:00:00:00.000"


def test_cxotime_descriptor_is_required_has_default_exception():
with pytest.raises(
ValueError, match="cannot set both 'required' and 'default' arguments"
):

@dataclass
class MyClass1:
time: CxoTime = CxoTimeDescriptor(default=100.0, required=True)