-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clean up and type number formatting APIs #20334
Clean up and type number formatting APIs #20334
Conversation
9fd6155
to
72ff7c1
Compare
The content of this PR is basically my best crack at #20322 (comment)
|
72ff7c1
to
b06cd03
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for figuring this out! Aside from a few implementation fixmes this goes into a good direction. I'd like to clean it up more, but that doesn't have to block this PR, it should be a follow-up.
pkg/lib/cockpit.js
Outdated
if (!is_object(options)) | ||
options = { separate: options }; | ||
|
||
options.factor = second_arg; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks unclean: If options is already an object, it is passed in from the caller. This will modify that passed-in argument. So as an intermediate step, this is safer: options = {...options, factor: second_arg}
(i.e. make a copy).
But even more so this is a field which isn't documented or covered by types, so doesn't this raise some ts alarm?
WDYT about something like
if (second_arg === 1024)
options = {...options, base2: true }
else
console.warn("format_units(): Ignoring invalid factor value", second_arg);
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. This is a good suggestion and I'm surprised I was sloppy about this.
function format_number(n: MaybeNumber, precision?: number): string | ||
function format_bytes(n: MaybeNumber, options?: FormatOptions): string; | ||
function format_bytes_per_sec(n: MaybeNumber, options?: FormatOptions): string; | ||
function format_bits_per_sec(n: MaybeNumber, options?: FormatOptions & { base2?: false }): string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, so the argument here is. if I wanted to provide an options
argument, I'd have to figure out the default factor
which would be error prone?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new API basically has two possibilities: SI units (default, old factor=1000
) and IEC units (old factor=1024
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jelly With the "old" API you could specify undefined
for the factor, in which case it would be the default "1000". But indeed this is error prone -- in fact, my first version here gave null
, which made it fall over completely. That's why I'd like to get rid of that.
b06cd03
to
99dff27
Compare
This has a single user (in storaged) and is more general than it needs to be to support that user. Hardcode it to SI-based units and move it into storaged/utils.js, so we can keep the unit tests. The unit tests themselves only test the 1024 case (which is the opposite of what is actually used in practice) so adjust them for SI (mostly by renaming variables and changing `1024`s to `1000`s).
these failures are actually relevant -- first of all, a unit conversion got lost along the way (see screenshot), and now I agree that we we need to restrict (or disable) the cockpit.warning(). Sorry for the bad advice. |
Thanks to Martin for some ideas there.
Add a small function called f() to qunit-tests.ts. It is intended to be used as a tagged template string format function. The default formatting for template strings passes each argument to String(), which means that objects get formatted like: [object Object] This function formats them using JSON, so you can see the actual object value. It can be imported into any QUnit test and used like: f`format_bytes_per_sec(${checks[i][0]}, ${checks[i][1]}, ${checks[i][2]})` which produces output like: ok 121 - format_bytes_per_sec: format_bytes_per_sec(25555678, "kB/s", {"precision":2})
Use our fancy new tagged templates to name our assertions in test-format. In addition to being a lot easier (and shorter) to write, it means we see `{"precision": 2}` instead of `[object Object]`. Also, leave out the value that we're comparing it equal to. This value is already displayed on assertion failures, and having it shown twice only makes the output harder to read.
This comes down to export function format_fsys_usage(used, total) {
let text = "";
let parts = cockpit.format_bytes(total, undefined, { separate: true, precision: 2 });
text = " / " + parts.join(" ");
const unit = parts[1];
parts = cockpit.format_bytes(used, unit, { separate: true, precision: 2 });
return parts[0] + text;
} ie: another API doing the same thing as the usage you were trying to eliminate from podman in cockpit-project/cockpit-podman#1673. cc @garrett |
Let's make sure we don't regress this with changes to `cockpit.format_units()`.
Add a new simplified style for all of the units-based number formatting APIs (bytes, bytes/sec, bits/sec). Keep loose versions of the old APIs around in `cockpit.d.ts`, but mark them deprecated. Make sure we test the new API variant from test-format.js.
This was pretty much completely pain-free, with a minor exception: the test overrides the `cockpit.language` attribute, which we would otherwise probably declare as `const`.
99dff27
to
3ca2dcb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brilliant, thanks! /devel failed with an unexpected message in TestTerminal.testBasic
("truncated data in external channel"), but I think that's an unrelated flake. The weather report shows the same message in TestSuperuser.testBasic
here and here.
I retry it to be sure.
@jelly can you please also check this again?
function format_number(n: MaybeNumber, precision?: number): string | ||
function format_bytes(n: MaybeNumber, options?: FormatOptions): string; | ||
function format_bytes_per_sec(n: MaybeNumber, options?: FormatOptions): string; | ||
function format_bits_per_sec(n: MaybeNumber, options?: FormatOptions & { base2?: false }): string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure I understand this: The base2
restriction is here to make base2: true
fall through the "deprecated" declaration, right? Not because enabling base2 would actually change the output type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure that enabling base2 will break this (ie: return just the number) in exactly the same way as passing 1024 as factor would. That particular function lacks base2 units (ie: no Mibps or so)...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, and I don't think we ever supported that actually.
@@ -1,5 +1,5 @@ | |||
import cockpit from "cockpit"; | |||
import QUnit from "qunit-tests"; | |||
import QUnit, { f } from "qunit-tests"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this f
? This aint no Python
// compat API: we used to accept 'factor' as a separate second arg | ||
if (third_arg || (second_arg && !is_object(second_arg))) { | ||
if (!deprecated_format_warned) { | ||
console.warn(`cockpit.format_{bytes,bits}[_per_sec](..., ${second_arg}, ${third_arg}) is deprecated.`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When do we remove it, should we include that? Like in 10 releases this API is gone?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with next release already...
cockpit.get_byte_units()
) with a single user (storaged
) into that user (along with its unit tests), and simplify it a bittest-format
to TypeScript