Question: Incorrect units read from MRT (CDS format) files with When reading MRT files (formatted according to the CDS standard which is also the format recommended by AAS/ApJ) with , incorrectly parses composite units. According to CDS standard the units should be SI without spaces (http://vizier.u-). Thus a unit of (surface brightness for a continuum measurement) should be written as . When I use these types of composite units with the reader the units do not come out correct. Specifically the order of the division seems to be jumbled. The units in the resulting Table should be the same as in the input MRT file. Get astropy package from pip Using the following MRT as input: And then reading the table I get: For the SBCONT column the second is in the wrong place, and for SBLINE kpc2 is in the wrong place.

del _lr_goto_items _lr_productions = [   ("S' -> main","S'",1,None,None,None),  ('main -> factor combined_units','main',2,'p_main','cds.py',156),  ('main -> combined_units','main',1,'p_main','cds.py',157),  ('main -> DIMENSIONLESS','main',1,'p_main','cds.py',158),  ('main -> OPEN_BRACKET combined_units CLOSE_BRACKET','main',3,'p_main','cds.py',159),  ('main -> OPEN_BRACKET DIMENSIONLESS CLOSE_BRACKET','main',3,'p_main','cds.py',160),  ('main -> factor','main',1,'p_main','cds.py',161),  ('combined_units -> product_of_units','combined_units',1,'p_combined_units','cds.py',174),  ('combined_units -> division_of_units','combined_units',1,'p_combined_units','cds.py',175),  ('product_of_units -> unit_expression PRODUCT combined_units','product_of_units',3,'p_product_of_units','cds.py',181),  ('product_of_units -> unit_expression','product_of_units',1,'p_product_of_units','cds.py',182),  ('division_of_units -> DIVISION unit_expression','division_of_units',2,'p_division_of_units','cds.py',191),  ('division_of_units -> unit_expression DIVISION combined_units','division_of_units',3,'p_division_of_units','cds.py',192),  ('unit_expression -> unit_with_power','unit_expression',1,'p_unit_expression','cds.py',201),  ('unit_expression -> OPEN_PAREN combined_units CLOSE_PAREN','unit_expression',3,'p_unit_expression','cds.py',202),  ('factor -> signed_float X UINT signed_int','factor',4,'p_factor','cds.py',211),  ('factor -> UINT X UINT signed_int','factor',4,'p_factor','cds.py',212),  ('factor -> UINT signed_int','factor',2,'p_factor','cds.py',213),  ('factor -> UINT','factor',1,'p_factor','cds.py',214),  ('factor -> signed_float','factor',1,'p_factor','cds.py',215),  ('unit_with_power -> UNIT numeric_power','unit_with_power',2,'p_unit_with_power','cds.py',232),  ('unit_with_power -> UNIT','unit_with_power',1,'p_unit_with_power','cds.py',233),  ('numeric_power -> sign UINT','numeric_power',2,'p_numeric_power','cds.py',242),  ('sign -> SIGN','sign',1,'p_sign','cds.py',248),  ('sign -> <empty>','sign',0,'p_sign','cds.py',249),  ('signed_int -> SIGN UINT','signed_int',2,'p_signed_int','cds.py',258),  ('signed_float -> sign UINT','signed_float',2,'p_signed_float','cds.py',264),  ('signed_float -> sign UFLOAT','signed_float',2,'p_signed_float','cds.py',265),  ('main -> factor combined_units','main',2,'p_main','cds.py',147),  ('main -> combined_units','main',1,'p_main','cds.py',148),  ('main -> DIMENSIONLESS','main',1,'p_main','cds.py',149),  ('main -> OPEN_BRACKET combined_units CLOSE_BRACKET','main',3,'p_main','cds.py',150),  ('main -> OPEN_BRACKET DIMENSIONLESS CLOSE_BRACKET','main',3,'p_main','cds.py',151),  ('main -> factor','main',1,'p_main','cds.py',152),  ('combined_units -> product_of_units','combined_units',1,'p_combined_units','cds.py',166),  ('combined_units -> division_of_units','combined_units',1,'p_combined_units','cds.py',167),  ('product_of_units -> unit_expression PRODUCT combined_units','product_of_units',3,'p_product_of_units','cds.py',173),  ('product_of_units -> unit_expression','product_of_units',1,'p_product_of_units','cds.py',174),  ('division_of_units -> DIVISION unit_expression','division_of_units',2,'p_division_of_units','cds.py',183),  ('division_of_units -> combined_units DIVISION unit_expression','division_of_units',3,'p_division_of_units','cds.py',184),  ('unit_expression -> unit_with_power','unit_expression',1,'p_unit_expression','cds.py',193),  ('unit_expression -> OPEN_PAREN combined_units CLOSE_PAREN','unit_expression',3,'p_unit_expression','cds.py',194),  ('factor -> signed_float X UINT signed_int','factor',4,'p_factor','cds.py',203),  ('factor -> UINT X UINT signed_int','factor',4,'p_factor','cds.py',204),  ('factor -> UINT signed_int','factor',2,'p_factor','cds.py',205),  ('factor -> UINT','factor',1,'p_factor','cds.py',206),  ('factor -> signed_float','factor',1,'p_factor','cds.py',207),  ('unit_with_power -> UNIT numeric_power','unit_with_power',2,'p_unit_with_power','cds.py',222),  ('unit_with_power -> UNIT','unit_with_power',1,'p_unit_with_power','cds.py',223),  ('numeric_power -> sign UINT','numeric_power',2,'p_numeric_power','cds.py',232),  ('sign -> SIGN','sign',1,'p_sign','cds.py',238),  ('sign -> <empty>','sign',0,'p_sign','cds.py',239),  ('signed_int -> SIGN UINT','signed_int',2,'p_signed_int','cds.py',248),  ('signed_float -> sign UINT','signed_float',2,'p_signed_float','cds.py',254),  ('signed_float -> sign UFLOAT','signed_float',2,'p_signed_float','cds.py',255), ]


Question: Add ` Or at least use the maximum precision of the matrix dtype instead of the arbitrarily chosen 1e-15.

return Angle(angle, u.radian), -axis / r  def is_O3(matrix):def is_O3(matrix, atol=None):     """Check whether a matrix is in the length-preserving group O(3).      Parameters


Question: Add ` Or at least use the maximum precision of the matrix dtype instead of the arbitrarily chosen 1e-15.

matrix : (..., N, N) array-like         Must have attribute ``.shape`` and method ``.swapaxes()`` and not error         when using `~numpy.isclose`.<ins>    atol : float, optional        The allowed absolute difference.        If `None` it defaults to 1e-15 or 5 * epsilon of the matrix's dtype, if floating.        .. versionadded:: 5.3</ins>      Returns     -------


Question: Add ` Or at least use the maximum precision of the matrix dtype instead of the arbitrarily chosen 1e-15.

"""     # matrix is in O(3) (rotations, proper and improper).     I = np.identity(matrix.shape[-1])    if atol is None:        if np.issubdtype(matrix.dtype, np.floating):            atol = np.finfo(matrix.dtype).eps * 5        else:            atol = 1e-15     is_o3 = np.all(        np.isclose(matrix @ matrix.swapaxes(-2, -1), I, atol=1e-15), axis=(-2, -1)        np.isclose(matrix @ matrix.swapaxes(-2, -1), I, atol=atol), axis=(-2, -1)     )      return is_o3  def is_rotation(matrix, allow_improper=False):def is_rotation(matrix, allow_improper=False, atol=None):     """Check whether a matrix is a rotation, proper or improper.      Parameters


Question: Add ` Or at least use the maximum precision of the matrix dtype instead of the arbitrarily chosen 1e-15.

Whether to restrict check to the SO(3), the group of proper rotations,         or also allow improper rotations (with determinant -1).         The default (False) is only SO(3).<ins>    atol : float, optional        The allowed absolute difference.        If `None` it defaults to 1e-15 or 5 * epsilon of the matrix's dtype, if floating.        .. versionadded:: 5.3</ins>      Returns     -------


Question: Add ` Or at least use the maximum precision of the matrix dtype instead of the arbitrarily chosen 1e-15.

For more information, see https://en.wikipedia.org/wiki/Orthogonal_group      """    if atol is None:        if np.issubdtype(matrix.dtype, np.floating):            atol = np.finfo(matrix.dtype).eps * 5        else:            atol = 1e-15     # matrix is in O(3).    is_o3 = is_O3(matrix)    is_o3 = is_O3(matrix, atol=atol)      # determinant checks  for rotation (proper and improper)     if allow_improper:  # determinant can be +/- 1        is_det1 = np.isclose(np.abs(np.linalg.det(matrix)), 1.0)        is_det1 = np.isclose(np.abs(np.linalg.det(matrix)), 1.0, atol=atol)     else:  # restrict to SO(3)        is_det1 = np.isclose(np.linalg.det(matrix), 1.0)        is_det1 = np.isclose(np.linalg.det(matrix), 1.0, atol=atol)      return is_o3 & is_det1


Question: Space between value and unit Currently, ` doesn't include a space between the value and unit: I think there are cases where it would make sense to allow a space to be included, so this is a feature request to add a boolean keyword argument to optionally add a space. Note that Quantity does include a space by default so maybe actually we should just change the default and not add an option?

)             func = ("{:g}" if precision is None else f"{{0:0.{precision}f}}").format             # Don't add unit by default for decimal.            # TODO: could we use Quantity.to_string() here?             if not (decimal and format is None):                 unit_string = unit.to_string(format=format)                 if format == "latex" or format == "latex_inline":                    unit_string = unit_string[1:-1]                    # Remove $ and add space in front if unit is not a superscript.                    if "^" in unit_string:                        unit_string = unit_string[1:-1]                    else:                        unit_string = r"\;" + unit_string[1:-1]                elif len(unit_string) > 1:                    # Length one for angular units can only happen for                    # superscript degree, arcmin, arcsec, hour, minute, second,                    # and those should not get an extra space.                    unit_string = " " + unit_string                 format_func = func                 func = lambda x: format_func(x) + unit_string


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

>>> import astropy.units as u       >>> print(u.Ry.decompose().to_string('console'))  # doctest: +FLOAT_CMP      2.1798721*10^-18m^2 kg s^-2      2.1798721*10^-18 m^2 kg s^-2       >>> print(u.Ry.decompose().to_string('console', inline=False))  # doctest: +FLOAT_CMP                        m^2 kg       2.1798721*10^-18 ------


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

def _get_unit_name(cls, unit):         return unit.get_format_name("console") <ins>    @classmethod    def _format_mantissa(cls, m):        return m</ins>     @classmethod     def _format_superscript(cls, number):         return f"^{number}"


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

parts = []         if m:            parts.append(m)            parts.append(cls._format_mantissa(m))          if ex:             parts.append(f"10{cls._format_superscript(ex)}")


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

s = cls.format_exponential_notation(unit.scale)              if len(unit.bases):<ins>                if s:                    s += " "</ins>                 if inline:                     nominator = zip(unit.bases, unit.powers)                     denominator = []


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

nominator = "1"                     denominator = cls._format_unit_list(denominator)                     fraclength = max(len(nominator), len(denominator))                    f = f"{{0:^{len(s)}s}} {{1:^{fraclength}s}}"                    f = f"{{0:<{len(s)}s}}{{1:^{fraclength}s}}"                      lines = [                         f.format("", nominator),diff --git a/astropy/units/format/latex.py b/astropy/units/format/latex.py-- a/astropy/units/format/latex.py++ b/astropy/units/format/latex.py


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

if unit.scale == 1:                 s = ""             else:                s = cls.format_exponential_notation(unit.scale) + r"\,"                s = cls.format_exponential_notation(unit.scale)              if len(unit.bases):                if s:                    s += r"\,"                 if inline:                     nominator = zip(unit.bases, unit.powers)                     denominator = []diff --git a/astropy/units/format/unicode_format.py b/astropy/units/format/unicode_format.py-- a/astropy/units/format/unicode_format.py++ b/astropy/units/format/unicode_format.py


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

"""  from . import console, utilsfrom . import console   class Unicode(console.Console):


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

>>> import astropy.units as u       >>> print(u.bar.decompose().to_string('unicode'))      100000kg m⁻¹ s⁻²      100000 kg m⁻¹ s⁻²       >>> print(u.bar.decompose().to_string('unicode', inline=False))               kg       100000 ────


Question: Unicode and console unit representations sometimes include an extraneous space As noted in , for units typset in or format, a space is included in front, unlike for regular units, yet it is sometimes omitted if a unit scale factor is present. should never start with a space, independent of . From and Any astropy really.

return unit.get_format_name("unicode")      @classmethod    def format_exponential_notation(cls, val):        m, ex = utils.split_mantissa_exponent(val)        parts = []        if m:            parts.append(m.replace("-", "−"))        if ex:            parts.append(f"10{cls._format_superscript(ex)}")        return cls._times.join(parts)    def _format_mantissa(cls, m):        return m.replace("-", "−")      @classmethod     def _format_superscript(cls, number):        mapping = {            "0": "⁰",            "1": "¹",            "2": "²",            "3": "³",            "4": "⁴",            "5": "⁵",            "6": "⁶",            "7": "⁷",            "8": "⁸",            "9": "⁹",            "-": "⁻",            "−": "⁻",            # This is actually a "raised omission bracket", but it's            # the closest thing I could find to a superscript solidus.            "/": "⸍",        }        output = []        for c in number:            output.append(mapping[c])        return "".join(output)        mapping = str.maketrans(            {                "0": "⁰",                "1": "¹",                "2": "²",                "3": "³",                "4": "⁴",                "5": "⁵",                "6": "⁶",                "7": "⁷",                "8": "⁸",                "9": "⁹",                "-": "⁻",                "−": "⁻",                # This is actually a "raised omission bracket", but it's                # the closest thing I could find to a superscript solidus.                "/": "⸍",            }        )        return number.translate(mapping)


Question: Regarding FITS standard definition for 'Jy/beam' Astropy unit definition for Jy/beam currently exports it as 'beam-1 Jy' 'beam-1 Jy' This is contrary to how most radio astronomy packages define the unit. 'Jy/beam' seems to be the accepted convention. The space after beam-1 makes parsing needlessly cumbersome as well. Is this something that can be fixed? See related issues opened in SpectralCube and SoFiA2.

>>> from astropy import units as u     >>> bb = models.BlackBody(temperature=5000*u.K)     >>> bb(6000 * u.AA)  # doctest: +FLOAT_CMP    <Quantity 1.53254685e-05 erg / (cm2 Hz s sr)>    <Quantity 1.53254685e-05 erg / (Hz s sr cm2)>      .. plot::         :include-source:diff --git a/astropy/units/format/generic.py b/astropy/units/format/generic.py-- a/astropy/units/format/generic.py++ b/astropy/units/format/generic.py


Question: Regarding FITS standard definition for 'Jy/beam' Astropy unit definition for Jy/beam currently exports it as 'beam-1 Jy' 'beam-1 Jy' This is contrary to how most radio astronomy packages define the unit. 'Jy/beam' seems to be the accepted convention. The space after beam-1 makes parsing needlessly cumbersome as well. Is this something that can be fixed? See related issues opened in SpectralCube and SoFiA2.

else:                     raise ValueError(f"Syntax error parsing unit '{s}'") <del>    @classmethod    def _format_unit_list(cls, units):        units.sort(key=lambda x: cls._get_unit_name(x[0]).lower())        return super()._format_unit_list(units)</del>  # 2023-02-18: The statement in the docstring is no longer true, the class is not used # anywhere so can be safely removed in 6.0.


Question: New Quantity warning starting with yesterday's numpy-dev Starting today, CI tests with and started failing due a new warning. I've extracted a MWE showing the warning: The warning is: The warning is not emitted with and stable (1.24.2). CC:

np.put, np.fill_diagonal, np.tile, np.repeat,     np.split, np.array_split, np.hsplit, np.vsplit, np.dsplit,     np.stack, np.column_stack, np.hstack, np.vstack, np.dstack,    np.amax, np.amin, np.ptp, np.sum, np.cumsum,    np.max, np.min, np.amax, np.amin, np.ptp, np.sum, np.cumsum,     np.prod, np.product, np.cumprod, np.cumproduct,     np.round, np.around,    np.round_,  # Alias for np.round in NUMPY_LT_1_25, but deprecated since.     np.fix, np.angle, np.i0, np.clip,     np.isposinf, np.isneginf, np.isreal, np.iscomplex,     np.average, np.mean, np.std, np.var, np.median, np.trace,


Question: may use a string representation of floats that is larger than necessary In some scenarios, may use a string representation of floats that is larger than necessary, which can force comments to be truncated. Due to this, there are some keyword/value/comment combinations that are impossible to create via even though they are entirely possible in FITS. Being able to create any valid FITS Card via . contains the following card in the header: We can read the header of this file and get this card without any issue: However, we have problems creating a object with exactly the same contents of : Essentially the value "0." is being unnecessarily expanded to "0.", which forces the comment to be truncated. I've investigated the source code and the root issue is the function which creates a of when is used as the input: It seems to me that before doing , we should attempt to use the string representation provided by Python (in other words ), and we should only attempt to format it ourselves if the resulting string does not fit in 20 characters. However, since this is fairly deep in the code, it's possible this would have side-effects that I am not aware of. Windows-10-10.0.-SP0 Python 3.10.10 (tags/v3.10.10:, Feb 7 2023, 17:20:36) [MSC v.1929 64 bit (AMD64)] astropy 5.2.1 Numpy 1.24.2 pyerfa 2.0.0.1 Scipy 1.10.0 Matplotlib 3.6.3

def _format_float(value):    """Format a floating number to make sure it gets the decimal point."""    value_str = f"{value:.16G}"    if "." not in value_str and "E" not in value_str:        value_str += ".0"    elif "E" in value_str:        # On some Windows builds of Python (and possibly other platforms?) the        # exponent is zero-padded out to, it seems, three digits.  Normalize        # the format to pad only to two digits.        significand, exponent = value_str.split("E")        if exponent[0] in ("+", "-"):            sign = exponent[0]            exponent = exponent[1:]        else:            sign = ""        value_str = f"{significand}E{sign}{int(exponent):02d}"    """Format a floating number to make sure it is at most 20 characters."""    value_str = str(value).replace("e", "E")      # Limit the value string to at most 20 characters.    str_len = len(value_str)    if str_len > 20:    if (str_len := len(value_str)) > 20:         idx = value_str.find("E")         if idx < 0:            # No scientific notation, truncate decimal places             value_str = value_str[:20]         else:            # Scientific notation, truncate significand (mantissa)             value_str = value_str[: 20 - (str_len - idx)] + value_str[idx:]      return value_str


Question: creates a corrupt FITS files if a contains zero-dimensional data accepts a ndarray with shape (zero-dimensional) as a data array. This later causes issues when writing to a file because assumes that the data has at least 1 dimension, resulting in a corrupt FITS file. should never silently create a corrupt FITS file. Minimal reproducible example: Although this causes no errors/warnings, the resulting file is not valid FITS and will fail to properly open with . Windows-10-10.0.-SP0 Python 3.10.10 (tags/v3.10.10:, Feb 7 2023, 17:20:36) [MSC v.1929 64 bit (AMD64)] astropy 5.2.1 Numpy 1.24.2 pyerfa 2.0.0.1 Scipy 1.10.0 Matplotlib 3.6.3

self._data_replaced = True             was_unsigned = False         if (            data is not None            and not isinstance(data, np.ndarray)            and not _is_dask_array(data)        ):            # Try to coerce the data into a numpy array--this will work, on            # some level, for most objects            try:                data = np.array(data)            except Exception:                raise TypeError(                    f"data object {data!r} could not be coerced into an ndarray"                )        if data is not None:            if not isinstance(data, np.ndarray) and not _is_dask_array(data):                # Try to coerce the data into a numpy array--this will work, on                # some level, for most objects                try:                    data = np.array(data)                except Exception:  # pragma: no cover                    raise TypeError(                        f"data object {data!r} could not be coerced into an " f"ndarray"                    )              if data.shape == ():                 raise TypeError(


Question: may sometimes report differences between identical files In some scenarios, may report differences between identical files, even when comparing the same file to itself. This may be caused by improper handling of VLAs (variable-length arrays). only reports differences in files if they exist. Comparing a file to itself should never yield a difference. Prints out: I suspect the handling of VLAs is the culprit here as I couldn't reproduce the bug without using at least one VLA column. Windows-10-10.0.-SP0 Python 3.10.10 (tags/v3.10.10:, Feb 7 2023, 17:20:36) [MSC v.1929 64 bit (AMD64)] astropy 5.2.1 Numpy 1.24.2 pyerfa 2.0.0.1 Scipy 1.10.0 Matplotlib 3.6.3

arrb.dtype, np.floating             ):                 diffs = where_not_allclose(arra, arrb, rtol=self.rtol, atol=self.atol)            elif "P" in col.format:            elif "P" in col.format or "Q" in col.format:                 diffs = (                     [                         idx


Question: Problem with "decimalyear" applied to MaskedColumn type The "decimalyear" type produces odd errors when applied to the MaskedColumn type. The "jyear" type does not, and appears to behave properly. I would expect "decimalyear" and "jyear" to work similarly, although the difference between them is not clear from the documentation. Linux-5.19.0-35-generic-x86_64-with-glibc2.35 Python 3.9.13 (main, Aug 25 2022, 23:26:10) [GCC 11.2.0] astropy 5.1

----------     val1 : numpy ndarray, list, number, str, or bytes         Values to initialize the time or times.  Bytes are decoded as ascii.<ins>        Quantities with time units are allowed for formats where the        interpretation is unambiguous.</ins>     val2 : numpy ndarray, list, or number; optional         Value(s) to initialize the time or times.  Only used for numerical         input, to help preserve precision.


Question: Problem with "decimalyear" applied to MaskedColumn type The "decimalyear" type produces odd errors when applied to the MaskedColumn type. The "jyear" type does not, and appears to behave properly. I would expect "decimalyear" and "jyear" to work similarly, although the difference between them is not clear from the documentation. Linux-5.19.0-35-generic-x86_64-with-glibc2.35 Python 3.9.13 (main, Aug 25 2022, 23:26:10) [GCC 11.2.0] astropy 5.1

class TimeJD(TimeNumeric):     """     Julian Date time format.<ins></ins>     This represents the number of days since the beginning of     the Julian Period.     For example, 2451544.5 in JD is midnight on January 1, 2000.


Question: Problem with "decimalyear" applied to MaskedColumn type The "decimalyear" type produces odd errors when applied to the MaskedColumn type. The "jyear" type does not, and appears to behave properly. I would expect "decimalyear" and "jyear" to work similarly, although the difference between them is not clear from the documentation. Linux-5.19.0-35-generic-x86_64-with-glibc2.35 Python 3.9.13 (main, Aug 25 2022, 23:26:10) [GCC 11.2.0] astropy 5.1

class TimeMJD(TimeNumeric):     """     Modified Julian Date time format.<ins></ins>     This represents the number of days since midnight on November 17, 1858.     For example, 51544.0 in MJD is midnight on January 1, 2000.     """


Question: Problem with "decimalyear" applied to MaskedColumn type The "decimalyear" type produces odd errors when applied to the MaskedColumn type. The "jyear" type does not, and appears to behave properly. I would expect "decimalyear" and "jyear" to work similarly, although the difference between them is not clear from the documentation. Linux-5.19.0-35-generic-x86_64-with-glibc2.35 Python 3.9.13 (main, Aug 25 2022, 23:26:10) [GCC 11.2.0] astropy 5.1

value = property(to_value)  def _check_val_type_not_quantity(format_name, val1, val2):    # If val2 is a Quantity, the super() call that follows this check    # will raise a TypeError.    if hasattr(val1, "to") and getattr(val1, "unit", None) is not None:        raise ValueError(            f"cannot use Quantities for {format_name!r} format, as the unit of year "            "is defined as 365.25 days, while the length of year is variable "            "in this format. Use float instead."        ) class TimeDecimalYear(TimeNumeric):     """     Time as a decimal year, with integer values corresponding to midnight    of the first day of each year.  For example 2000.5 corresponds to the    ISO time '2000-07-02 00:00:00'.    of the first day of each year.    For example 2000.5 corresponds to the ISO time '2000-07-02 00:00:00'.    Since for this format the length of the year varies between 365 and    366 days, it is not possible to use Quantity input, in which a year    is always 365.25 days.     """      name = "decimalyear"     def _check_val_type(self, val1, val2):        _check_val_type_not_quantity(self.name, val1, val2)        # if val2 is a Quantity, super() will raise a TypeError.        return super()._check_val_type(val1, val2)     def set_jds(self, val1, val2):         self._check_scale(self._scale)  # Validate scale.


Question: Problem with "decimalyear" applied to MaskedColumn type The "decimalyear" type produces odd errors when applied to the MaskedColumn type. The "jyear" type does not, and appears to behave properly. I would expect "decimalyear" and "jyear" to work similarly, although the difference between them is not clear from the documentation. Linux-5.19.0-35-generic-x86_64-with-glibc2.35 Python 3.9.13 (main, Aug 25 2022, 23:26:10) [GCC 11.2.0] astropy 5.1

class TimeFromEpoch(TimeNumeric):     """     Base class for times that represent the interval from a particular    epoch as a floating point multiple of a unit time interval (e.g. seconds    epoch as a numerical multiple of a unit time interval (e.g. seconds     or days).     """


Question: Problem with "decimalyear" applied to MaskedColumn type The "decimalyear" type produces odd errors when applied to the MaskedColumn type. The "jyear" type does not, and appears to behave properly. I would expect "decimalyear" and "jyear" to work similarly, although the difference between them is not clear from the documentation. Linux-5.19.0-35-generic-x86_64-with-glibc2.35 Python 3.9.13 (main, Aug 25 2022, 23:26:10) [GCC 11.2.0] astropy 5.1

class TimeEpochDate(TimeNumeric):     """    Base class for support floating point Besselian and Julian epoch dates.    Base class for support of Besselian and Julian epoch dates.     """      _default_scale = "tt"  # As of astropy 3.2, this is no longer 'utc'.


Question: Problem with "decimalyear" applied to MaskedColumn type The "decimalyear" type produces odd errors when applied to the MaskedColumn type. The "jyear" type does not, and appears to behave properly. I would expect "decimalyear" and "jyear" to work similarly, although the difference between them is not clear from the documentation. Linux-5.19.0-35-generic-x86_64-with-glibc2.35 Python 3.9.13 (main, Aug 25 2022, 23:26:10) [GCC 11.2.0] astropy 5.1

class TimeBesselianEpoch(TimeEpochDate):    """Besselian Epoch year as floating point value(s) like 1950.0."""    """Besselian Epoch year as value(s) like 1950.0.    Since for this format the length of the year varies, input needs to    be floating point; it is not possible to use Quantity input, for    which a year always equals 365.25 days.    """      name = "byear"     epoch_to_jd = "epb2jd"     jd_to_epoch = "epb"      def _check_val_type(self, val1, val2):        """Input value validation, typically overridden by derived classes."""        if hasattr(val1, "to") and hasattr(val1, "unit") and val1.unit is not None:            raise ValueError(                "Cannot use Quantities for 'byear' format, as the interpretation "                "would be ambiguous. Use float with Besselian year instead."            )        _check_val_type_not_quantity(self.name, val1, val2)         # FIXME: is val2 really okay here?         return super()._check_val_type(val1, val2)   class TimeJulianEpoch(TimeEpochDate):    """Julian Epoch year as floating point value(s) like 2000.0."""    """Julian Epoch year as value(s) like 2000.0."""      name = "jyear"     unit = erfa.DJY  # 365.25, the Julian year, for conversion to quantities


Question: Writing a Table to FITS fails if the table contains objects The following works fine: whereas the following fails: with This behaviour is seen whenever a Table contains an object, i.e. does not know how to deal with . I wonder if we want the Table API to write objects to files by their string representation as a default, or otherwise provide a more meaningful error message?

for idx in range(len(array.dtype)):             cname = array.dtype.names[idx]             ftype = array.dtype.fields[cname][0]            format = self._col_format_cls.from_recformat(ftype)            if ftype.kind == "O":                dtypes = {np.array(array[cname][i]).dtype for i in range(len(array))}                if (len(dtypes) > 1) or (np.dtype("O") in dtypes):                    raise TypeError(                        f"Column '{cname}' contains unsupported object types or "                        f"mixed types: {dtypes}"                    )                ftype = dtypes.pop()                format = self._col_format_cls.from_recformat(ftype)                format = f"P{format}()"            else:                format = self._col_format_cls.from_recformat(ftype)              # Determine the appropriate dimensions for items in the column             dim = array.dtype[idx].shape[::-1]


Question: TST: check fails with numpy-dev (TypeError: cannot write to unmasked output) Started popping up in numpy-dev jobs. is investigating.

)         return result.any(axis=-1)     def _combine_masks(self, masks, out=None):    def _combine_masks(self, masks, out=None, where=True, copy=True):        """Combine masks, possibly storing it in some output.        Parameters        ----------        masks : tuple of array of bool or None            Input masks.  Any that are `None` or `False` are ignored.            Should broadcast to each other.        out : output mask array, optional            Possible output array to hold the result.        where : array of bool, optional            Which elements of the output array to fill.        copy : bool optional            Whether to ensure a copy is made. Only relevant if a single            input mask is not `None`, and ``out`` is not given.        """         masks = [m for m in masks if m is not None and m is not False]         if not masks:             return False         if len(masks) == 1:             if out is None:                return masks[0].copy()                return masks[0].copy() if copy else masks[0]             else:                np.copyto(out, masks[0])                np.copyto(out, masks[0], where=where)                 return out         out = np.logical_or(masks[0], masks[1], out=out)        out = np.logical_or(masks[0], masks[1], out=out, where=where)         for mask in masks[2:]:            np.logical_or(out, mask, out=out)            np.logical_or(out, mask, out=out, where=where)         return out      def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):


Question: TST: check fails with numpy-dev (TypeError: cannot write to unmasked output) Started popping up in numpy-dev jobs. is investigating.

elif out_mask is None:                     out_mask = m <ins>        # TODO: where is only needed for __call__ and reduce;        # this is very fast, but still worth separating out?        where = kwargs.pop("where", True)        if where is True:            where_unmasked = True            where_mask = None        else:            where_unmasked, where_mask = self._get_data_and_mask(where)</ins>         unmasked, masks = self._get_data_and_masks(*inputs)          if ufunc.signature:


Question: TST: check fails with numpy-dev (TypeError: cannot write to unmasked output) Started popping up in numpy-dev jobs. is investigating.

else np.logical_or.reduce(mask1)                     )                 mask = self._combine_masks(masks, out=out_mask)                mask = self._combine_masks(masks, out=out_mask, copy=False)              else:                 # Parse signature with private numpy function. Note it


Question: TST: check fails with numpy-dev (TypeError: cannot write to unmasked output) Started popping up in numpy-dev jobs. is investigating.

elif method == "__call__":             # Regular ufunc call.            mask = self._combine_masks(masks, out=out_mask)            # Combine the masks from the input, possibly selecting elements.            mask = self._combine_masks(masks, out=out_mask, where=where_unmasked)            # If relevant, also mask output elements for which where was masked.            if where_mask is not None:                mask |= where_mask          elif method == "outer":             # Must have two arguments; adjust masks as will be done for data.


Question: TST: check fails with numpy-dev (TypeError: cannot write to unmasked output) Started popping up in numpy-dev jobs. is investigating.

elif method in {"reduce", "accumulate"}:             # Reductions like np.add.reduce (sum).            if masks[0] is not None:            # Treat any masked where as if the input element was masked.            mask = self._combine_masks((masks[0], where_mask), copy=False)            if mask is not False:                 # By default, we simply propagate masks, since for                 # things like np.sum, it makes no sense to do otherwise.                 # Individual methods need to override as needed.                # TODO: take care of 'out' too?                 if method == "reduce":                     axis = kwargs.get("axis", None)                     keepdims = kwargs.get("keepdims", False)                    where = kwargs.get("where", True)                     mask = np.logical_or.reduce(                        masks[0],                        where=where,                        mask,                        where=where_unmasked,                         axis=axis,                         keepdims=keepdims,                         out=out_mask,                     )                    if where is not True:                        # Mask also whole rows that were not selected by where,                        # so would have been left as unmasked above.                        mask |= np.logical_and.reduce(                            masks[0], where=where, axis=axis, keepdims=keepdims                    if where_unmasked is not True:                        # Mask also whole rows in which no elements were selected;                        # those will have been left as unmasked above.                        mask |= ~np.logical_or.reduce(                            where_unmasked, axis=axis, keepdims=keepdims                         )                  else:                     # Accumulate                     axis = kwargs.get("axis", 0)                    mask = np.logical_or.accumulate(masks[0], axis=axis, out=out_mask)                    mask = np.logical_or.accumulate(mask, axis=axis, out=out_mask)             elif out is not None:                mask = False            else:  # pragma: no cover            elif out is None:                 # Can only get here if neither input nor output was masked, but                # perhaps axis or where was masked (in NUMPY_LT_1_21 this is                # possible).  We don't support this.                # perhaps where was masked (possible in "not NUMPY_LT_1_25" and                # in NUMPY_LT_1_21 (latter also allowed axis).                # We don't support this.                 return NotImplemented          elif method in {"reduceat", "at"}:  # pragma: no cover            # TODO: implement things like np.add.accumulate (used for cumsum).             raise NotImplementedError(                 "masked instances cannot yet deal with 'reduceat' or 'at'."             )          if out_unmasked is not None:             kwargs["out"] = out_unmasked        if where_unmasked is not True:            kwargs["where"] = where_unmasked         result = getattr(ufunc, method)(*unmasked, **kwargs)          if result is None:  # pragma: no cover


Question: Inconsistency in double single-quote ('') management in FITS Card The management of single-quotes in FITS cards seem correct, except sometimes when dealing with null strings, i.e. double single quotes (), which sometimes are transformed into single single quotes (). E.g.: gives If the null string is included in a larger value, the issue occurs at a different position: gives All card values should be handled properly. Linux-5.10.0-1029-oem-x86_64-with-glibc2.29 Python 3.8.10 (default, Mar 13 2023, 10:26:41) [GCC 9.4.0] astropy 5.2.1 Numpy 1.23.5 pyerfa 2.0.0 Scipy 1.10.0 Matplotlib 3.6.2

# followed by an optional comment     _strg = r"\'(?P<strg>([ -~]+?|\'\'|) *?)\'(?=$|/| )"     _comm_field = r"(?P<comm_field>(?P<sepr>/ *)(?P<comm>(.|\n)*))"    _strg_comment_RE = re.compile(f"({_strg})? *{_comm_field}?")    _strg_comment_RE = re.compile(f"({_strg})? *{_comm_field}?$")      # FSC commentary card string which must contain printable ASCII characters.     # Note: \Z matches the end of the string without allowing newlines


Question: Inconsistency in double single-quote ('') management in FITS Card The management of single-quotes in FITS cards seem correct, except sometimes when dealing with null strings, i.e. double single quotes (), which sometimes are transformed into single single quotes (). E.g.: gives If the null string is included in a larger value, the issue occurs at a different position: gives All card values should be handled properly. Linux-5.10.0-1029-oem-x86_64-with-glibc2.29 Python 3.8.10 (default, Mar 13 2023, 10:26:41) [GCC 9.4.0] astropy 5.2.1 Numpy 1.23.5 pyerfa 2.0.0 Scipy 1.10.0 Matplotlib 3.6.2

return kw, vc                  value = m.group("strg") or ""                value = value.rstrip().replace("''", "'")                value = value.rstrip()                 if value and value[-1] == "&":                     value = value[:-1]                 values.append(value)


Question: Make getting a topocentric ITRS position easier Right now, it is not easy to create ITRS coordinates for sources relative to a given location (rather than geocentric), to the level that we have specific instructions on how to calculate relative and then put these into an : This has led to numerous issues, the latest of which is It would be nice if as part of it would be possible to get a topocentric rather than a geocentric position. In , and (and below) to extend to take not just an but also a argument, with an implementation along : See . Labeling this a good first issue since it is easy code wise. However, writing the tests and documentation will require understanding of how ITRS and the associated coordinate transformations work.

"""Convert to a tuple with X, Y, and Z as quantities."""         return (self.x, self.y, self.z)     def get_itrs(self, obstime=None):    def get_itrs(self, obstime=None, location=None):         """         Generates an `~astropy.coordinates.ITRS` object with the location of        this object at the requested ``obstime``.        this object at the requested ``obstime``, either geocentric, or        topocentric relative to a given ``location``.          Parameters         ----------         obstime : `~astropy.time.Time` or None             The ``obstime`` to apply to the new `~astropy.coordinates.ITRS`, or             if None, the default ``obstime`` will be used.        location : `~astropy.coordinates.EarthLocation` or None            A possible observer's location, for a topocentric ITRS position.            If not given (default), a geocentric ITRS object will be created.          Returns         -------         itrs : `~astropy.coordinates.ITRS`            The new object in the ITRS frame            The new object in the ITRS frame, either geocentric or topocentric            relative to the given ``location``.         """         # Broadcast for a single position at multiple times, but don't attempt         # to be more general here.


Question: Make getting a topocentric ITRS position easier Right now, it is not easy to create ITRS coordinates for sources relative to a given location (rather than geocentric), to the level that we have specific instructions on how to calculate relative and then put these into an : This has led to numerous issues, the latest of which is It would be nice if as part of it would be possible to get a topocentric rather than a geocentric position. In , and (and below) to extend to take not just an but also a argument, with an implementation along : See . Labeling this a good first issue since it is easy code wise. However, writing the tests and documentation will require understanding of how ITRS and the associated coordinate transformations work.

# do this here to prevent a series of complicated circular imports         from .builtin_frames import ITRS         return ITRS(x=self.x, y=self.y, z=self.z, obstime=obstime)        if location is None:            # No location provided, return geocentric ITRS coordinates            return ITRS(x=self.x, y=self.y, z=self.z, obstime=obstime)        else:            return ITRS(                self.x - location.x,                self.y - location.y,                self.z - location.z,                copy=False,                obstime=obstime,                location=location,            )      itrs = property(         get_itrs,


Question: Register Cosmology can now read and write to files. It would be nice to register with a method for exporting a Cosmology to a Latex table. There are good examples of IO with Cosmology at and documentation at I'm thinking the H0(..., format='latex')`.

"""  # Import to register with the I/O machineryfrom . import cosmology, ecsv, html, mapping, model, row, table, yamlfrom . import cosmology, ecsv, html, mapping, model, row, table, yaml, latexdiff --git a/astropy/cosmology/io/latex.py b/astropy/cosmology/io/latex.pynew file mode 100644-- /dev/null++ b/astropy/cosmology/io/latex.py


Question: Register Cosmology can now read and write to files. It would be nice to register with a method for exporting a Cosmology to a Latex table. There are good examples of IO with Cosmology at and documentation at I'm thinking the H0(..., format='latex')`.

<ins>import astropy.units as ufrom astropy.cosmology.connect import readwrite_registryfrom astropy.cosmology.core import Cosmologyfrom astropy.cosmology.parameter import Parameterfrom astropy.table import QTablefrom .table import to_table_FORMAT_TABLE = {    "H0": "$$H_0$$",    "Om0": "$$\\Omega_{m,0}$$",    "Ode0": "$$\\Omega_{\\Lambda,0}$$",    "Tcmb0": "$$T_{0}$$",    "Neff": "$$N_{eff}$$",    "m_nu": "$$m_{nu}$$",    "Ob0": "$$\\Omega_{b,0}$$",    "w0": "$$w_{0}$$",    "wa": "$$w_{a}$$",    "wz": "$$w_{z}$$",    "wp": "$$w_{p}$$",    "zp": "$$z_{p}$$",}def write_latex(    cosmology, file, *, overwrite=False, cls=QTable, latex_names=True, **kwargs):    r"""Serialize the |Cosmology| into a LaTeX.    Parameters    ----------    cosmology : `~astropy.cosmology.Cosmology` subclass instance    file : path-like or file-like        Location to save the serialized cosmology.    overwrite : bool        Whether to overwrite the file, if it exists.    cls : type, optional keyword-only        Astropy :class:`~astropy.table.Table` (sub)class to use when writing.        Default is :class:`~astropy.table.QTable`.    latex_names : bool, optional keyword-only        Whether to use LaTeX names for the parameters. Default is `True`.    **kwargs        Passed to ``cls.write``    Raises    ------    TypeError        If kwarg (optional) 'cls' is not a subclass of `astropy.table.Table`    """    # Check that the format is 'latex' (or not specified)    format = kwargs.pop("format", "latex")    if format != "latex":        raise ValueError(f"format must be 'latex', not {format}")    # Set cosmology_in_meta as false for now since there is no metadata being kept    table = to_table(cosmology, cls=cls, cosmology_in_meta=False)    cosmo_cls = type(cosmology)    for name, col in table.columns.copy().items():        param = getattr(cosmo_cls, name, None)        if not isinstance(param, Parameter) or param.unit in (None, u.one):            continue        # Get column to correct unit        table[name] <<= param.unit    # Convert parameter names to LaTeX format    if latex_names:        new_names = [_FORMAT_TABLE.get(k, k) for k in cosmology.__parameters__]        table.rename_columns(cosmology.__parameters__, new_names)    table.write(file, overwrite=overwrite, format="latex", **kwargs)# ===================================================================# Registerreadwrite_registry.register_writer("latex", Cosmology, write_latex)</ins>


Question: BUG: change representation of vs table.Table More often than not it's rather confusing (and annoying) that a VOTable and a Table look exactly the same, but obviously, they don't behave the same way and don't have the same methods available, etc. I would suggest to change the votable case of to something else, e.g. .

warn_unknown_attrs("TABLE", extra.keys(), config, pos)      def __repr__(self):        return repr(self.to_table())        s = repr(self.to_table())        if s.startswith("<Table"):            s = "<VO" + s[1:]        return s      def __bytes__(self):         return bytes(self.to_table())


Question: TST: testtablegroupby[True] and testgroupbymasked[True] failed with numpy 1.25rc1 I see this in the predeps job that pulls in numpy 1.25rc1. Example log: Hard to discern between the other 100+ failures from and I do not understand why we didn't catch this earlier in devdeps. , does this look familiar to you? TST: testtablegroupby[True] and testgroupbymasked[True] failed with numpy 1.25rc1 I see this in the predeps job that pulls in numpy 1.25rc1. Example log: Hard to discern between the other 100+ failures from and I do not understand why we didn't catch this earlier in devdeps. , does this look familiar to you?

raise ValueError("Cannot create index without at least one column")         elif len(columns) == 1:             col = columns[0]            row_index = Column(col.argsort())            row_index = Column(col.argsort(kind="stable"))             data = Table([col[row_index]])         else:             num_rows = len(columns[0])


Question: TST: testtablegroupby[True] and testgroupbymasked[True] failed with numpy 1.25rc1 I see this in the predeps job that pulls in numpy 1.25rc1. Example log: Hard to discern between the other 100+ failures from and I do not understand why we didn't catch this earlier in devdeps. , does this look familiar to you? TST: testtablegroupby[True] and testgroupbymasked[True] failed with numpy 1.25rc1 I see this in the predeps job that pulls in numpy 1.25rc1. Example log: Hard to discern between the other 100+ failures from and I do not understand why we didn't catch this earlier in devdeps. , does this look familiar to you?

try:                 lines = table[np.lexsort(sort_columns)]             except TypeError:  # arbitrary mixins might not work with lexsort                lines = table[table.argsort()]                lines = table[table.argsort(kind="stable")]             data = lines[lines.colnames[:-1]]             row_index = lines[lines.colnames[-1]] diff --git a/astropy/time/core.py b/astropy/time/core.py-- a/astropy/time/core.py++ b/astropy/time/core.py


Question: TST: testtablegroupby[True] and testgroupbymasked[True] failed with numpy 1.25rc1 I see this in the predeps job that pulls in numpy 1.25rc1. Example log: Hard to discern between the other 100+ failures from and I do not understand why we didn't catch this earlier in devdeps. , does this look familiar to you? TST: testtablegroupby[True] and testgroupbymasked[True] failed with numpy 1.25rc1 I see this in the predeps job that pulls in numpy 1.25rc1. Example log: Hard to discern between the other 100+ failures from and I do not understand why we didn't catch this earlier in devdeps. , does this look familiar to you?

return dt.argmax(axis, out)     def argsort(self, axis=-1):    def argsort(self, axis=-1, kind="stable"):         """Returns the indices that would sort the time array.         This is similar to :meth:`~numpy.ndarray.argsort`, but adapted to ensure        that the full precision given by the two doubles ``jd1`` and ``jd2``        is used, and that corresponding attributes are copied.  Internally,        it uses :func:`~numpy.lexsort`, and hence no sort method can be chosen.        This is similar to :meth:`~numpy.ndarray.argsort`, but adapted to ensure that        the full precision given by the two doubles ``jd1`` and ``jd2`` is used, and        that corresponding attributes are copied.  Internally, it uses        :func:`~numpy.lexsort`, and hence no sort method can be chosen.        Parameters        ----------        axis : int, optional            Axis along which to sort. Default is -1, which means sort along the last            axis.        kind : 'stable', optional            Sorting is done with :func:`~numpy.lexsort` so this argument is ignored, but            kept for compatibility with :func:`~numpy.argsort`. The sorting is stable,            meaning that the order of equal elements is preserved.        Returns        -------        indices : ndarray            An array of indices that sort the time array.         """         # For procedure, see comment on argmin.         jd1, jd2 = self.jd1, self.jd2


Question: Add registration label ‘’ to Cosmology IO The Cosmology write methods that leverage Table should have the same keys. Table has both “latex” and “”, so too should Cosmology. Register the method a second time, under No response

TypeError         If kwarg (optional) 'cls' is not a subclass of `astropy.table.Table`     """    # Check that the format is 'latex' (or not specified)    # Check that the format is 'latex', 'ascii.latex' (or not specified)     format = kwargs.pop("format", "latex")    if format != "latex":        raise ValueError(f"format must be 'latex', not {format}")    if format not in ("latex", "ascii.latex"):        raise ValueError(f"format must be 'latex' or 'ascii.latex', not {format}")      # Set cosmology_in_meta as false for now since there is no metadata being kept     table = to_table(cosmology, cls=cls, cosmology_in_meta=False)


Question: Add registration label ‘’ to Cosmology IO The Cosmology write methods that leverage Table should have the same keys. Table has both “latex” and “”, so too should Cosmology. Register the method a second time, under No response

# Register  readwrite_registry.register_writer("latex", Cosmology, write_latex)<ins>readwrite_registry.register_writer("ascii.latex", Cosmology, write_latex)</ins>


Question: QTable: Group keys don't have a unit When grouping a QTable with a column that contains a quantity, the keys only contain a float with a value, but not the full quantity: The keys should be complete, i.e. for a quantity column it should be a quantity with the proper unit. No response Linux-6.1.0-9-amd64-x86_64-with-glibc2.36 (Debian bookworm) Python 3.11.4 (main, Jun 7 2023, 10:13:09) [GCC 12.2.0] astropy 5.2.1 (also checked with 5.3) Numpy 1.24.2 pyerfa 2.0.0.3 Scipy 1.10.1 Matplotlib 3.6.3

)         )     # TODO: don't use represent_mixins_as_columns here, but instead ensure that    # keys_sort.argsort(kind="stable") works for all columns (including mixins).     # If there is not already an available index and table_keys is a Table then ensure     # that all cols (including mixins) are in a form that can sorted with the code below.     if not table_index and isinstance(table_keys, Table):        table_keys = represent_mixins_as_columns(table_keys)        table_keys_sort = represent_mixins_as_columns(table_keys)    else:        table_keys_sort = table_keys      # Get the argsort index `idx_sort`, accounting for particulars     try:


Question: QTable: Group keys don't have a unit When grouping a QTable with a column that contains a quantity, the keys only contain a float with a value, but not the full quantity: The keys should be complete, i.e. for a quantity column it should be a quantity with the proper unit. No response Linux-6.1.0-9-amd64-x86_64-with-glibc2.36 (Debian bookworm) Python 3.11.4 (main, Jun 7 2023, 10:13:09) [GCC 12.2.0] astropy 5.2.1 (also checked with 5.3) Numpy 1.24.2 pyerfa 2.0.0.3 Scipy 1.10.1 Matplotlib 3.6.3

if table_index is not None:             idx_sort = table_index.sorted_data()         else:            idx_sort = table_keys.argsort(kind="mergesort")            idx_sort = table_keys_sort.argsort(kind="stable")         stable_sort = True     except TypeError:        # TODO: is this still needed?         # Some versions (likely 1.6 and earlier) of numpy don't support         # 'mergesort' for all data types.  MacOSX (Darwin) doesn't have a stable         # sort by default, nor does Windows, while Linux does (or appears to).        idx_sort = table_keys.argsort()        idx_sort = table_keys_sort.argsort()         stable_sort = platform.system() not in ("Darwin", "Windows")      # Finally do the actual sort of table_keys values


Question: QTable: Group keys don't have a unit When grouping a QTable with a column that contains a quantity, the keys only contain a float with a value, but not the full quantity: The keys should be complete, i.e. for a quantity column it should be a quantity with the proper unit. No response Linux-6.1.0-9-amd64-x86_64-with-glibc2.36 (Debian bookworm) Python 3.11.4 (main, Jun 7 2023, 10:13:09) [GCC 12.2.0] astropy 5.2.1 (also checked with 5.3) Numpy 1.24.2 pyerfa 2.0.0.3 Scipy 1.10.1 Matplotlib 3.6.3

from .serialize import represent_mixins_as_columns     from .table import Table     if isinstance(keys, Table):        keys = represent_mixins_as_columns(keys)        keys = keys.as_array()    # TODO: don't use represent_mixins_as_columns here, but instead ensure that    # keys_sort.argsort(kind="stable") works for all columns (including mixins).     if not isinstance(keys, np.ndarray):        raise TypeError(f"Keys input must be numpy array, but got {type(keys)}")    if isinstance(keys, Table):        keys_sort = represent_mixins_as_columns(keys)    else:        keys_sort = keys     if len(keys) != len(column):    if len(keys_sort) != len(column):         raise ValueError(             "Input keys array length {} does not match column length {}".format(                 len(keys), len(column)             )         )     idx_sort = keys.argsort()    try:        idx_sort = keys_sort.argsort(kind="stable")    except AttributeError:        raise TypeError(            f"keys input ({keys.__class__.__name__}) must have an `argsort` method"        )     keys = keys[idx_sort]      # Get all keys


Question: Error in distance calculations for w0wz Cosmologies I believe that the equation used to calculate the dedensityscale in is incorrect. Line 205 has After manually calculating the integral/checking wolfram, I don't think it should be a negative in the exponent and should read: package from '...' run '...' error occurs. import platform; print(platform.platform()) import sys; print("Python", sys.version) import astropy; print("astropy", astropy.version) import numpy; print("Numpy", numpy.version) import erfa; print("pyerfa", erfa.version) import scipy; print("Scipy", scipy.version) import matplotlib; print("Matplotlib", matplotlib.version)

The scaling of the energy density of dark energy with redshift.             Returns `float` if the input is scalar.         References        ----------        .. [1] Linder, E. (2003). Exploring the Expansion History of the Universe.               Physics Review Letters, 90(9), 091301.         Notes         -----         The scaling factor, I, is defined by :math:`\rho(z) = \rho_0 I`,        and in this case is given by        and in this case is given by ([1]_)          .. math::             I = \left(1 + z\right)^{3 \left(1 + w_0 - w_z\right)}                     \exp \left(-3 w_z z\right)                     \exp \left(3 w_z z\right)         """         z = aszarr(z)        zp1 = z + 1.0  # (converts z [unit] -> z [dimensionless])        return zp1 ** (3.0 * (1.0 + self._w0 - self._wz)) * exp(-3.0 * self._wz * z)        return (z + 1.0) ** (3.0 * (1.0 + self._w0 - self._wz)) * exp(            3.0 * self._wz * z        )   class Flatw0wzCDM(FlatFLRWMixin, w0wzCDM):


Question: In v5.3, NDDataRef mask propagation fails when one of the operand does not have a mask This applies to v5.3. It looks like when one of the operand does not have a mask, the mask propagation when doing arithmetic, in particular with fails. This is not a problem in v5.2. I don't know enough about how all that works, but it seems from the error that the operand without a mask is set as a mask of None's and then the bitwise_or tries to operate on an integer and a None and fails. When one of the operand does not have mask, the mask that exists should just be copied over to the output. Or whatever was done in that situation in v5.2 where there's no problem. This is with v5.3. With v5.2, there are no errors. import sys; print("Python", sys.version) Python 3.10.11 (main, May 10 2023, 19:07:22) [Clang 14.0.6 ] import astropy; print("astropy", astropy.version) astropy 5.3 import numpy; print("Numpy", numpy.version) Numpy 1.24.3 import erfa; print("pyerfa", erfa.version) pyerfa 2.0.0.3 import scipy; print("Scipy", scipy.version) Scipy 1.10.1 import matplotlib; print("Matplotlib", matplotlib.version) Matplotlib 3.7.1

elif self.mask is None and operand is not None:             # Make a copy so there is no reference in the result.             return deepcopy(operand.mask)        elif operand is None:        elif operand.mask is None:             return deepcopy(self.mask)         else:            # Now lets calculate the resulting mask (operation enforces copy)            # Now let's calculate the resulting mask (operation enforces copy)             return handle_mask(self.mask, operand.mask, **kwds)      def _arithmetic_wcs(self, operation, operand, compare_wcs, **kwds):


Question: Possible bug in related to D exponents I came across the following code in : I think this may be incorrect because as far as I can tell (it returns a copy). Commenting out this code doesn't cause any tests to fail so I think this code isn't being tested anyway.

# Replace exponent separator in floating point numbers         if 'D' in format:            output_field.replace(encode_ascii('E'), encode_ascii('D'))            output_field[:] = output_field.replace(b'E', b'D')   def _get_recarray_field(array, key):


Question: Context manager for constant versions For some use cases it would be helpful to have a context manager to set the version set of the constants. E.g., something like `

<Quantity 0.510998927603161 MeV>  """import itertoolsimport inspectfrom contextlib import contextmanager  # Hack to make circular imports with units work try:


Question: Context manager for constant versions For some use cases it would be helpful to have a context manager to set the version set of the constants. E.g., something like `

except ImportError:     pass from .constant import Constant, EMConstantfrom . import sifrom . import cgsfrom . import codata2014, iau2015from .constant import Constant, EMConstant  # noqafrom . import si  # noqafrom . import cgs  # noqafrom . import codata2014, iau2015  # noqafrom . import utils as _utils  # for updating the constants module docstring _lines = [


Question: Context manager for constant versions For some use cases it would be helpful to have a context manager to set the version set of the constants. E.g., something like `

'========== ============== ================ =========================', ] for _nm, _c in itertools.chain(sorted(vars(codata2014).items()),                               sorted(vars(iau2015).items())):    if isinstance(_c, Constant) and _c.abbrev not in locals():        locals()[_c.abbrev] = _c.__class__(_c.abbrev, _c.name, _c.value,                                           _c._unit_string, _c.uncertainty,                                           _c.reference)        _lines.append('{0:^10} {1:^14.9g} {2:^16} {3}'.format(            _c.abbrev, _c.value, _c._unit_string, _c.name))# NOTE: Update this when default changes._utils._set_c(codata2014, iau2015, inspect.getmodule(inspect.currentframe()),              not_in_module_only=True, doclines=_lines, set_class=True)  _lines.append(_lines[1])  if __doc__ is not None:     __doc__ += '\n'.join(_lines) del _lines, _nm, _c# TODO: Re-implement in a way that is more consistent with astropy.units.#       See https://github.com/astropy/astropy/pull/7008 discussions.@contextmanagerdef set_enabled_constants(modname):    """    Context manager to temporarily set values in the ``constants``    namespace to an older version.    See :ref:`astropy-constants-prior` for usage.    Parameters    ----------    modname : {'astropyconst13'}        Name of the module containing an older version.    """    # Re-import here because these were deleted from namespace on init.    import inspect    import warnings    from . import utils as _utils    # NOTE: Update this when default changes.    if modname == 'astropyconst13':        from .astropyconst13 import codata2010 as codata        from .astropyconst13 import iau2012 as iaudata    else:        raise ValueError(            'Context manager does not currently handle {}'.format(modname))    module = inspect.getmodule(inspect.currentframe())    # Ignore warnings about "Constant xxx already has a definition..."    with warnings.catch_warnings():        warnings.simplefilter('ignore')        _utils._set_c(codata, iaudata, module,                      not_in_module_only=False, set_class=True)    try:        yield    finally:        with warnings.catch_warnings():            warnings.simplefilter('ignore')            # NOTE: Update this when default changes.            _utils._set_c(codata2014, iau2015, module,                          not_in_module_only=False, set_class=True)# Clean up namespacedel inspectdel contextmanagerdel _utilsdel _linesdiff --git a/astropy/constants/astropyconst13.py b/astropy/constants/astropyconst13.py-- a/astropy/constants/astropyconst13.py++ b/astropy/constants/astropyconst13.py


Question: Context manager for constant versions For some use cases it would be helpful to have a context manager to set the version set of the constants. E.g., something like `

See :mod:`astropy.constants` for a complete listing of constants defined in Astropy. """import itertoolsfrom .constant import Constantimport inspectfrom . import utils as _utils from . import codata2010, iau2012 for _nm, _c in itertools.chain(sorted(vars(codata2010).items()),                               sorted(vars(iau2012).items())):    if (isinstance(_c, Constant) and _c.abbrev not in locals()):        locals()[_c.abbrev] = _c_utils._set_c(codata2010, iau2012, inspect.getmodule(inspect.currentframe()))# Clean up namespacedel inspectdel _utilsdiff --git a/astropy/constants/astropyconst20.py b/astropy/constants/astropyconst20.py-- a/astropy/constants/astropyconst20.py++ b/astropy/constants/astropyconst20.py


Question: Context manager for constant versions For some use cases it would be helpful to have a context manager to set the version set of the constants. E.g., something like `

Astronomical and physics constants for Astropy v2.0.  See :mod:`astropy.constants` for a complete listing of constants defined in Astropy. """import itertoolsfrom .constant import Constantimport inspectfrom . import utils as _utils from . import codata2014, iau2015 for _nm, _c in itertools.chain(sorted(vars(codata2014).items()),                               sorted(vars(iau2015).items())):    if (isinstance(_c, Constant) and _c.abbrev not in locals()):        locals()[_c.abbrev] = _c_utils._set_c(codata2014, iau2015, inspect.getmodule(inspect.currentframe()))# Clean up namespacedel inspectdel _utilsdiff --git a/astropy/constants/utils.py b/astropy/constants/utils.pynew file mode 100644-- /dev/null++ b/astropy/constants/utils.py


Question: Context manager for constant versions For some use cases it would be helpful to have a context manager to set the version set of the constants. E.g., something like `

<ins># Licensed under a 3-clause BSD style license - see LICENSE.rst"""Utility functions for ``constants`` sub-package."""import itertools__all__ = []def _get_c(codata, iaudata, module, not_in_module_only=True):    """    Generator to return a Constant object.    Parameters    ----------    codata, iaudata : obj        Modules containing CODATA and IAU constants of interest.    module : obj        Namespace module of interest.    not_in_module_only : bool        If ``True``, ignore constants that are already in the        namespace of ``module``.    Returns    -------    _c : Constant        Constant object to process.    """    from .constant import Constant    for _nm, _c in itertools.chain(sorted(vars(codata).items()),                                   sorted(vars(iaudata).items())):        if not isinstance(_c, Constant):            continue        elif (not not_in_module_only) or (_c.abbrev not in module.__dict__):            yield _cdef _set_c(codata, iaudata, module, not_in_module_only=True, doclines=None,           set_class=False):    """    Set constants in a given module namespace.    Parameters    ----------    codata, iaudata : obj        Modules containing CODATA and IAU constants of interest.    module : obj        Namespace module to modify with the given ``codata`` and ``iaudata``.    not_in_module_only : bool        If ``True``, constants that are already in the namespace        of ``module`` will not be modified.    doclines : list or `None`        If a list is given, this list will be modified in-place to include        documentation of modified constants. This can be used to update        docstring of ``module``.    set_class : bool        Namespace of ``module`` is populated with ``_c.__class__``        instead of just ``_c`` from :func:`_get_c`.    """    for _c in _get_c(codata, iaudata, module,                     not_in_module_only=not_in_module_only):        if set_class:            value = _c.__class__(_c.abbrev, _c.name, _c.value,                                 _c._unit_string, _c.uncertainty,                                 _c.reference)        else:            value = _c        setattr(module, _c.abbrev, value)        if doclines is not None:            doclines.append('{0:^10} {1:^14.9g} {2:^16} {3}'.format(                _c.abbrev, _c.value, _c._unit_string, _c.name))</ins>


Question: InheritDocstrings metaclass doesn't work for properties Inside the InheritDocstrings metaclass it uses which returns for properties.

A "grab bag" of relatively small general-purpose utilities that don't have a clear module/package to live in. """<del></del> import abc import contextlib import difflib


Question: InheritDocstrings metaclass doesn't work for properties Inside the InheritDocstrings metaclass it uses which returns for properties.

from collections import defaultdict, OrderedDict  <del></del> __all__ = ['isiterable', 'silence', 'format_exception', 'NumpyRNGContext',            'find_api_page', 'is_path_hidden', 'walk_skip_hidden',            'JsonCustomEncoder', 'indent', 'InheritDocstrings',


Question: InheritDocstrings metaclass doesn't work for properties Inside the InheritDocstrings metaclass it uses which returns for properties.

not key.startswith('_'))          for key, val in dct.items():            if (inspect.isfunction(val) and                is_public_member(key) and                val.__doc__ is None):            if ((inspect.isfunction(val) or inspect.isdatadescriptor(val)) and                    is_public_member(key) and                    val.__doc__ is None):                 for base in cls.__mro__[1:]:                     super_method = getattr(base, key, None)                     if super_method is not None:


Question: units.quantityinput decorator fails for constructors with type hinted return value -None I am using the decorator with typing hints for constructors, however when I add the correct return value for the constructor () then I get an exception, because has no attribute . The issue can be reproduced with the following file: which results in the following error: This has been tested on Fedora 27 with python 3.6.3, astropy 2.0.2 and numpy 1.13.3 all from Fedora's repository. The issue can be circumvented by not adding the return type typing hint. Unfortunately, then a static type checker cannot infer that this function returns nothing. Maybe the decorator could explicitly check whether None is returned and then omit the unit check.

# Call the original function with any equivalencies in force.             with add_enabled_equivalencies(self.equivalencies):                 return_ = wrapped_function(*func_args, **func_kwargs)            if wrapped_signature.return_annotation is not inspect.Signature.empty:            if wrapped_signature.return_annotation not in (inspect.Signature.empty, None):                 return return_.to(wrapped_signature.return_annotation)             else:                 return return_


Question: should support conversion to Because of the inheritance has a method which is useless. It should have a method which returns a object or objects. Conversion to (for ) and (for ) would also be great.

import copy import operatorfrom datetime import datetimefrom datetime import datetime, timedelta  import numpy as np


Question: should support conversion to Because of the inheritance has a method which is useless. It should have a method which returns a object or objects. Conversion to (for ) and (for ) would also be great.

other = getattr(other, out.scale)         else:             if other.scale is None:                    out._set_scale('tai')                out._set_scale('tai')             else:                 if self.scale not in TIME_TYPES[other.scale]:                     raise TypeError("Cannot add Time and TimeDelta instances "


Question: should support conversion to Because of the inheritance has a method which is useless. It should have a method which returns a object or objects. Conversion to (for ) and (for ) would also be great.

The allowed values for ``format`` can be listed with::        >>> list(TimeDelta.FORMATS)      ['sec', 'jd']      ['sec', 'jd', 'datetime']      Note that for time differences, the scale can be among three groups:     geocentric ('tai', 'tt', 'tcg'), barycentric ('tcb', 'tdb'), and rotational


Question: should support conversion to Because of the inheritance has a method which is useless. It should have a method which returns a object or objects. Conversion to (for ) and (for ) would also be great.

info = TimeDeltaInfo()      def __init__(self, val, val2=None, format=None, scale=None, copy=False):<ins>        if isinstance(val, timedelta) and not format:            format = 'datetime'</ins>         if isinstance(val, TimeDelta):             if scale is not None:                 self._set_scale(scale)


Question: should support conversion to Because of the inheritance has a method which is useless. It should have a method which returns a object or objects. Conversion to (for ) and (for ) would also be great.

out.SCALES = self.SCALES         return out     def to_datetime(self):        """        Convert to ``datetime.timedelta`` object.        """        tm = self.replicate(format='datetime')        return tm._shaped_like_input(tm._time.value)     def _set_scale(self, scale):         """         This is the key routine that actually does time scale conversions.diff --git a/astropy/time/formats.py b/astropy/time/formats.py-- a/astropy/time/formats.py++ b/astropy/time/formats.py


Question: should support conversion to Because of the inheritance has a method which is useless. It should have a method which returns a object or objects. Conversion to (for ) and (for ) would also be great.

'TimeDeltaFormat', 'TimeDeltaSec', 'TimeDeltaJD',            'TimeEpochDateString', 'TimeBesselianEpochString',            'TimeJulianEpochString', 'TIME_FORMATS', 'TIME_DELTA_FORMATS',           'TimezoneInfo']           'TimezoneInfo', 'TimeDeltaDatetime']  __doctest_skip__ = ['TimePlotDate']


Question: should support conversion to Because of the inheritance has a method which is useless. It should have a method which returns a object or objects. Conversion to (for ) and (for ) would also be great.

unit = 1.  <ins>class TimeDeltaDatetime(TimeDeltaFormat, TimeUnique):    """Time delta in datetime.timedelta"""    name = 'datetime'    def _check_val_type(self, val1, val2):        # Note: don't care about val2 for this class        if not all(isinstance(val, datetime.timedelta) for val in val1.flat):            raise TypeError('Input values for {0} class must be '                            'datetime.timedelta objects'.format(self.name))        return val1, None    def set_jds(self, val1, val2):        self._check_scale(self._scale)  # Validate scale.        iterator = np.nditer([val1, None],                             flags=['refs_ok'],                             op_dtypes=[object] + [np.double])        for val, sec in iterator:            sec[...] = val.item().total_seconds()        self.jd1, self.jd2 = day_frac(iterator.operands[-1], 0.0,                                      divisor=erfa.DAYSEC)    @property    def value(self):        iterator = np.nditer([self.jd1 + self.jd2, None],                             flags=['refs_ok'],                             op_dtypes=[self.jd1.dtype] + [object])        for jd, out in iterator:            out[...] = datetime.timedelta(days=jd.item())        return self.mask_if_needed(iterator.operands[-1])</ins> from .core import Time, TIME_SCALES, TIME_DELTA_SCALES, ScaleValueError


Question: minversion failures The change in PR causes to fail in certain cases, e.g.: apparently because of a bug in LooseVersion (): Note that without the ".3" it doesn't fail: and using pkgresources.parseversion (which was removed) works: CC:

import inspect<ins>import re</ins> import types import importlib from distutils.version import LooseVersion


Question: minversion failures The change in PR causes to fail in certain cases, e.g.: apparently because of a bug in LooseVersion (): Note that without the ".3" it doesn't fail: and using pkgresources.parseversion (which was removed) works: CC:

else:         have_version = resolve_name(module.__name__, version_path) <ins>    # LooseVersion raises a TypeError when strings like dev, rc1 are part    # of the version number. Match the dotted numbers only. Regex taken    # from PEP440, https://www.python.org/dev/peps/pep-0440/, Appendix B    expr = '^([1-9]\\d*!)?(0|[1-9]\\d*)(\\.(0|[1-9]\\d*))*'    m = re.match(expr, version)    if m:        version = m.group(0)</ins>     if inclusive:         return LooseVersion(have_version) >= LooseVersion(version)     else:


Question: biweightlocation of a constant array returns nan Currently the robust mean estimator returns for an array with zero variance. eg: This is primarily because of a 0/0 division in the code (case when the calculated mad of array in denominator becomes zero). Wouldn't it be better to catch this special case and return the median, instead of returning nan?

# set up the weighting     mad = median_absolute_deviation(data, axis=axis)    if axis is None and mad == 0.:        return M  # return median if data is a constant array     if axis is not None:         mad = np.expand_dims(mad, axis=axis)        const_mask = (mad == 0.)        mad[const_mask] = 1.  # prevent divide by zero     u = d / (c * mad)      # now remove the outlier points


Question: biweightlocation of a constant array returns nan Currently the robust mean estimator returns for an array with zero variance. eg: This is primarily because of a 0/0 division in the code (case when the calculated mad of array in denominator becomes zero). Wouldn't it be better to catch this special case and return the median, instead of returning nan?

u = (1 - u ** 2) ** 2     u[mask] = 0 <ins>    # along the input axis if data is constant, d will be zero, thus    # the median value will be returned along that axis</ins>     return M.squeeze() + (d * u).sum(axis=axis) / u.sum(axis=axis)


Question: biweightlocation of a constant array returns nan Currently the robust mean estimator returns for an array with zero variance. eg: This is primarily because of a 0/0 division in the code (case when the calculated mad of array in denominator becomes zero). Wouldn't it be better to catch this special case and return the median, instead of returning nan?

# set up the weighting     mad = median_absolute_deviation(data, axis=axis)    if axis is None and mad == 0.:        return 0.  # return zero if data is a constant array     if axis is not None:         mad = np.expand_dims(mad, axis=axis)        const_mask = (mad == 0.)        mad[const_mask] = 1.  # prevent divide by zero     u = d / (c * mad)      # now remove the outlier points


Question: biweightlocation of a constant array returns nan Currently the robust mean estimator returns for an array with zero variance. eg: This is primarily because of a 0/0 division in the code (case when the calculated mad of array in denominator becomes zero). Wouldn't it be better to catch this special case and return the median, instead of returning nan?

# set up the weighting     mad = median_absolute_deviation(data, axis=1)<ins>    const_mask = (mad == 0.)    mad[const_mask] = 1.  # prevent divide by zero</ins>     u = (d.T / (c * mad)).T      # now remove the outlier points


Question: Issue when passing empty lists/arrays to WCS transformations The following should not fail but instead should return empty lists/arrays:

"""          def _return_list_of_arrays(axes, origin):<ins>            if any([x.size == 0 for x in axes]):                return axes</ins>             try:                 axes = np.broadcast_arrays(*axes)             except ValueError:


Question: Issue when passing empty lists/arrays to WCS transformations The following should not fail but instead should return empty lists/arrays:

raise ValueError(                     "When providing two arguments, the array must be "                     "of shape (N, {0})".format(self.naxis))<ins>            if 0 in xy.shape:                return xy</ins>             if ra_dec_order and sky == 'input':                 xy = self._denormalize_sky(xy)             result = func(xy, origin)


Question: Issue when transforming a single scalar coordinate with a 1D WCS The following example illustrates a bug when dealing with single scalar coordinates in 1D WCSes: - would you have a chance to look at this?

raise TypeError(                     "When providing two arguments, they must be "                     "(coords[N][{0}], origin)".format(self.naxis))            if self.naxis == 1 and len(xy.shape) == 1:            if xy.shape == () or len(xy.shape) == 1:                 return _return_list_of_arrays([xy], origin)             return _return_single_array(xy, origin)


Question: Record data size in the WCS object It is clear that there are practical reasons to have a record of the original data size in the WCS object. This in the past has been recorded in public attributes and and subsequently in the private and . There's along thread on why this should or should not be done in . More recently expanded this attribute to more than 2 axes. It also broke the ability to set the private attributes which was subsequently fixed in as a stop gap measure. We need to fix this permanently starting with v 1.3. There are two proposed ways to do it. public attributes, populated by the FITS header keywords. a subclass of which adds those and leave the original WCS to represent strictly the FITS WCS standard and nothing more. Either way a deprecation process will accompany the change. The purpose of this issue is to collect votes. I don't want to start a new long discussion. Keep in mind that is available for reference and only share new arguments. Since 1.3 is approaching we need to make a fast decision. So please share your preference here and volunteer for the work if possible.

__doctest_skip__ = ['WCS.all_world2pix'] <ins>NAXIS_DEPRECATE_MESSAGE = """Private attributes "_naxis1" and "naxis2" have been deprecated since v3.1.Instead use the "pixel_shape" property which returns a list of NAXISj keyword values."""</ins>  if _wcs is not None:     _parsed_version = _wcs.__version__.split('.')


Question: Record data size in the WCS object It is clear that there are practical reasons to have a record of the original data size in the WCS object. This in the past has been recorded in public attributes and and subsequently in the private and . There's along thread on why this should or should not be done in . More recently expanded this attribute to more than 2 axes. It also broke the ability to set the private attributes which was subsequently fixed in as a stop gap measure. We need to fix this permanently starting with v 1.3. There are two proposed ways to do it. public attributes, populated by the FITS header keywords. a subclass of which adds those and leave the original WCS to represent strictly the FITS WCS standard and nothing more. Either way a deprecation process will accompany the change. The purpose of this issue is to collect votes. I don't want to start a new long discussion. Keep in mind that is available for reference and only share new arguments. Since 1.3 is approaching we need to make a fast decision. So please share your preference here and volunteer for the work if possible.

try:                     # classes that inherit from WCS and define naxis1/2                     # do not require a header parameter                    naxis1 = self._naxis1                    naxis2 = self._naxis2                except AttributeError:                    naxis1, naxis2 = self.pixel_shape                except (AttributeError, TypeError):                     warnings.warn("Need a valid header in order to calculate footprint\n", AstropyUserWarning)                     return None             else:


Question: Record data size in the WCS object It is clear that there are practical reasons to have a record of the original data size in the WCS object. This in the past has been recorded in public attributes and and subsequently in the private and . There's along thread on why this should or should not be done in . More recently expanded this attribute to more than 2 axes. It also broke the ability to set the private attributes which was subsequently fixed in as a stop gap measure. We need to fix this permanently starting with v 1.3. There are two proposed ways to do it. public attributes, populated by the FITS header keywords. a subclass of which adds those and leave the original WCS to represent strictly the FITS WCS standard and nothing more. Either way a deprecation process will accompany the change. The purpose of this issue is to collect votes. I don't want to start a new long discussion. Keep in mind that is available for reference and only share new arguments. Since 1.3 is approaching we need to make a fast decision. So please share your preference here and volunteer for the work if possible.

f.write(comments)             f.write('{}\n'.format(coordsys))             f.write('polygon(')            self.calc_footprint().tofile(f, sep=',')            f.write(') # color={0}, width={1:d} \n'.format(color, width))            ftpr = self.calc_footprint()            if ftpr is not None:                ftpr.tofile(f, sep=',')                f.write(') # color={0}, width={1:d} \n'.format(color, width))      @property     def _naxis1(self):        warnings.warn(NAXIS_DEPRECATE_MESSAGE, AstropyDeprecationWarning)         return self._naxis[0]      @_naxis1.setter     def _naxis1(self, value):        warnings.warn(NAXIS_DEPRECATE_MESSAGE, AstropyDeprecationWarning)         self._naxis[0] = value      @property     def _naxis2(self):        warnings.warn(NAXIS_DEPRECATE_MESSAGE, AstropyDeprecationWarning)         return self._naxis[1]      @_naxis2.setter     def _naxis2(self, value):        warnings.warn(NAXIS_DEPRECATE_MESSAGE, AstropyDeprecationWarning)         self._naxis[1] = value      def _get_naxis(self, header=None):


Question: Cosmologies used in equivalencies The thermodynamic temperature equivalency appears to default to the Planck 2015 cosmology value: whereas in the equivalency in , the default cosmology is used: and this is currently WMAP9. This seems inconsistent, so can we try and make things more consistent for the 3.1 release? (I see this as bug-like, so I think it can be addressed after feature freeze). Note that this is related to - if we change the default cosmology to Planck 2015, then we can change the equivalency to use the default cosmology without changing results (for now).

frequency : `~astropy.units.Quantity` with spectral units         The observed `spectral` equivalent `~astropy.units.Unit` (e.g.,         frequency or wavelength)    T_cmb :  `~astropy.units.Quantity` with temperature units (default Planck15 value)        The CMB temperature at z=0    T_cmb :  `~astropy.units.Quantity` with temperature units or None        The CMB temperature at z=0.  If `None`, the default cosmology will be        used to get this temperature.      Notes     -----


Question: Cosmologies used in equivalencies The thermodynamic temperature equivalency appears to default to the Planck 2015 cosmology value: whereas in the equivalency in , the default cosmology is used: and this is currently WMAP9. This seems inconsistent, so can we try and make things more consistent for the 3.1 release? (I see this as bug-like, so I think it can be addressed after feature freeze). Note that this is related to - if we change the default cosmology to Planck 2015, then we can change the equivalency to use the default cosmology without changing results (for now).

Planck HFI 143 GHz::          >>> from astropy import units as u        >>> from astropy.cosmology import Planck15         >>> freq = 143 * u.GHz        >>> equiv = u.thermodynamic_temperature(freq)        >>> equiv = u.thermodynamic_temperature(freq, Planck15.Tcmb0)         >>> (1. * u.mK).to(u.MJy / u.sr, equivalencies=equiv)  # doctest: +FLOAT_CMP         <Quantity 0.37993172 MJy / sr>


Question: Cosmologies used in equivalencies The thermodynamic temperature equivalency appears to default to the Planck 2015 cosmology value: whereas in the equivalency in , the default cosmology is used: and this is currently WMAP9. This seems inconsistent, so can we try and make things more consistent for the 3.1 release? (I see this as bug-like, so I think it can be addressed after feature freeze). Note that this is related to - if we change the default cosmology to Planck 2015, then we can change the equivalency to use the default cosmology without changing results (for now).

nu = frequency.to(si.GHz, spectral())      if T_cmb is None:        from ..cosmology import Planck15        T_cmb = Planck15.Tcmb0        from ..cosmology import default_cosmology        T_cmb = default_cosmology.get().Tcmb0      def f(nu, T_cmb=T_cmb):         x = _si.h * nu / _si.k_B / T_cmb


Question: FITS-standard unit parsing fails on some types of exponents Why don't these work: When these all do: The non-working versions give e.g.: which is not how I would interpret the . Tested on 2.0.7 and 3.0.3

def p_factor_fits(p):             '''             factor_fits : UINT power OPEN_PAREN signed_int CLOSE_PAREN                        | UINT power OPEN_PAREN UINT CLOSE_PAREN                         | UINT power signed_int                        | UINT power UINT                         | UINT SIGN UINT                         | UINT OPEN_PAREN signed_int CLOSE_PAREN             '''diff --git a/astropy/units/format/generic_parsetab.py b/astropy/units/format/generic_parsetab.py-- a/astropy/units/format/generic_parsetab.py++ b/astropy/units/format/generic_parsetab.py