Repository navigation
Simplify the to_string implementations in the various units formats - #14418
Conversation
|
Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.
|
nstarman
left a comment
There was a problem hiding this comment.
I have some questions, which maybe indicates some explanatory comments will be helpful for future maintainers.
Otherwise, I think this PR is good, especially given that no tests change.
| s = "" | ||
| if unit.scale == 1: | ||
| s = "" | ||
| else: |
There was a problem hiding this comment.
In personal use I haven't noticed a case when unit.scale != 1. From context I'm guessing this happens for exponentials, but more specifically... when is the scale not 1? Maybe a comment is warranted?
There was a problem hiding this comment.
You get it most often by decomposing. E.g.,
u.Ry.decompose()
# Unit("2.17987e-18 kg m2 / s2")
(the number is the scale).
| else: | ||
| s = cls.format_exponential_notation(unit.scale) | ||
|
|
||
| if len(unit.bases): |
There was a problem hiding this comment.
I tried bool(len(u.m.bases)) and this evaluates to True. When is len(unit.bases) False?
There was a problem hiding this comment.
Only for dimensionless - which can have a scale:
u.percent.decompose()
# Unit(dimensionless with a scale of 0.01)
f"{u.percent.decompose()}"
# 0.01
0202e91 to
6d870be
Compare
|
Rebased and adopted your suggestion -- but didn't add any comments yet. Let me know if having seen my answers, you still think it is useful. |
I think even just including the answers as comments would go a long way! |
|
OK, you're right, comments that explain what would happen are really always good. Added! |
| else: | ||
| nominator = cls._format_unit_list(nominator) | ||
| s += nominator | ||
|
|
There was a problem hiding this comment.
As this is very similar with what is in console.py and generic.py, couldn't this also united? By having a format_fraction(nominator, denominator) method that takes the specifics of console and latex?
class Generic(Base): […]
@classmethod
def to_string(cls, unit, inline=False):
parts = []
if unit.scale != 1:
parts.append(cls.format_exponential_notation(unit.scale))
if len(unit.bases):
if inline:
nominator = zip(unit.bases, unit.powers)
denominator = []
else:
nominator, denominator = utils.get_grouped_by_powers(
unit.bases, unit.powers
)
if len(denominator):
if not len(nominator):
nominator = ["1"]
parts.append(cls._format_fraction(nominator, denominator))
else:
parts.append(cls._format_unit_list(nominator))
return ' '.join(parts)
@classmethod
def _format_fraction(nominator, denominator):
if len(denominator == 1):
return f"{cls._format_unit_list(nominator)} / {cls._format_unit_list(denominator)}"
else:
return f"{cls._format_unit_list(nominator)} / ({cls._format_unit_list(denominator)})"
class Latex(Generic): […]
@classmethod
def _format_fraction(nominator, denominator):
return rf"\frac{{{cls._format_unit_list(nominator)}}}{{{cls._format_unit_list(denominator)}}}"
@classmethod
def to_string(cls, unit, inline=False, wrap=True):
s = super().to_string(unit, inline)
if wrap: # properly wrap into LaTeX mathmode
return rf"$\mathrm{{{s}}}$"
else
return s
class Console(Generic): […]
@classmethod
def _format_fraction(nominator, denominator):
nominator = cls._format_unit_list(denominator)
denominator = cls._format_unit_list(denominator)
fraclength = max(len(nominator), len(denominator))
f = f"{{0:<{len(s)}s}}{{1:^{fraclength}s}}"
return "\n".join(
(
f.format("", nominator),
f.format(s, cls._line * fraclength),
f.format("", denominator),
)
@classmethod
def to_string(cls, unit, inline=True):
return super().to_string(unit, inline)This would concentrate all the handling of the different options (with/without de/nominator, inline) in a general re-usable to_string() method and have the inline parameter more general. And for the LaTeX one has the easy option to switch off the wrapping, which could remove the hack of later removing it when a Quantity is formatted.
Or does this get too abstract then?
Update: By re-checking, I see that one would then somehow need to handle the multi-line concatenation of scale and base unit in generic.py instead of just joining the two parts here. For this, the part included now in Quantity formatting could be re-used; however this further complicates the structure.
There was a problem hiding this comment.
Hah, those were just my thoughts, and indeed the next step includes moving to_string() -- see #14416! (move to Base, since console, etc., shouldn't inherit the parsing form generic).
Aside, while so far (including #14416) the tests do not change, I have been thinking that perhaps, while we still can, we should rename inline -- it made sense for latex but I feel it is somewhat unclear for other formats, and it would also be nice to separate in-lline fractions from using negative powers. The best I could come up with so far is fraction=False, fraction='small' and fraction='big' (with fraction=True whatever makes most sense). Hopefully to extend to fits and ogip, where there is a wish to allow things like "Jy/beam" (see #13217).
| unit.bases, unit.powers | ||
| ) | ||
| if len(nominator): | ||
| parts.append(cls._format_unit_list(nominator)) |
There was a problem hiding this comment.
I don't understand why some of the class methods are public (cls.format_exponential_notation()) and others are marked as private (cls._format_unit_list()). Unless there is a specific reason here, shouldn't the format functions all have the same visibility?
There was a problem hiding this comment.
This is well before my time, but I think the idea was that the public methods are the only ones also used outside of the class - e.g., format_exponential_notation is not used only for the scale, but also by Quantity to format the values.
Maybe something to reconsider, though! (Perhaps best after finishing the refactor.)
pllim
left a comment
There was a problem hiding this comment.
I am approving by proxy. I am not sure why Ole doesn't get green check mark. I have inquired.
|
Thanks, all! |
Description
This pull request does as it says, mostly by avoiding separating out paths based on the unit class, since the more complicated path will work for all cases anyway. It is split off from #14416 to make review a bit easier.