oaknut-cli 12.8.2__tar.gz → 12.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oaknut_cli-12.8.2/src/oaknut_cli.egg-info → oaknut_cli-12.10.0}/PKG-INFO +2 -2
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/pyproject.toml +1 -1
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/src/oaknut/cli/__init__.py +5 -1
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/src/oaknut/cli/reports.py +44 -6
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0/src/oaknut_cli.egg-info}/PKG-INFO +2 -2
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/src/oaknut_cli.egg-info/requires.txt +1 -1
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/tests/test_reports.py +25 -1
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/LICENSE +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/README.md +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/setup.cfg +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/src/oaknut/cli/commands.py +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/src/oaknut/cli/help.py +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/src/oaknut_cli.egg-info/SOURCES.txt +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/src/oaknut_cli.egg-info/dependency_links.txt +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/src/oaknut_cli.egg-info/top_level.txt +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/tests/test_commands.py +0 -0
- {oaknut_cli-12.8.2 → oaknut_cli-12.10.0}/tests/test_help.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oaknut-cli
|
|
3
|
-
Version: 12.
|
|
3
|
+
Version: 12.10.0
|
|
4
4
|
Summary: Shared CLI toolkit for the oaknut family: the contributed-command axis and report-rendering helpers a disc command needs, below the filesystem packages.
|
|
5
5
|
Author-email: Robert Smallshire <robert@smallshire.org.uk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,7 +23,7 @@ Requires-Dist: oaknut-exception>=10.0
|
|
|
23
23
|
Requires-Dist: oaknut-extension>=10.0
|
|
24
24
|
Requires-Dist: oaknut-file>=10.0
|
|
25
25
|
Requires-Dist: click>=8.1.7
|
|
26
|
-
Requires-Dist: asyoulikeit>=1.
|
|
26
|
+
Requires-Dist: asyoulikeit>=1.4.0
|
|
27
27
|
Dynamic: license-file
|
|
28
28
|
|
|
29
29
|
# oaknut-cli
|
|
@@ -33,12 +33,14 @@ from oaknut.cli.reports import (
|
|
|
33
33
|
address_cell,
|
|
34
34
|
bytes_cell,
|
|
35
35
|
control_pictures,
|
|
36
|
+
datestamp_cell,
|
|
37
|
+
filetype_cell,
|
|
36
38
|
kv_table,
|
|
37
39
|
size_cell,
|
|
38
40
|
text_cell,
|
|
39
41
|
)
|
|
40
42
|
|
|
41
|
-
__version__ = "12.
|
|
43
|
+
__version__ = "12.10.0"
|
|
42
44
|
|
|
43
45
|
__all__ = [
|
|
44
46
|
"COMMAND_KIND",
|
|
@@ -51,6 +53,8 @@ __all__ = [
|
|
|
51
53
|
"address_cell",
|
|
52
54
|
"bytes_cell",
|
|
53
55
|
"control_pictures",
|
|
56
|
+
"datestamp_cell",
|
|
57
|
+
"filetype_cell",
|
|
54
58
|
"kv_table",
|
|
55
59
|
"size_cell",
|
|
56
60
|
"text_cell",
|
|
@@ -9,8 +9,11 @@ command can render output without depending on ``oaknut-disc``.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
+
from datetime import datetime, timedelta
|
|
13
|
+
|
|
12
14
|
from asyoulikeit import ByAudience
|
|
13
15
|
from oaknut.file.capacity import format_capacity
|
|
16
|
+
from oaknut.file.filetypes import filetype_name
|
|
14
17
|
|
|
15
18
|
#: Acorn discs are addressed in 256-byte sectors throughout.
|
|
16
19
|
SECTOR_SIZE = 256
|
|
@@ -71,14 +74,49 @@ def bytes_cell(num_bytes: int) -> ByAudience:
|
|
|
71
74
|
return ByAudience(machine=num_bytes, human=format_capacity(num_bytes))
|
|
72
75
|
|
|
73
76
|
|
|
74
|
-
def address_cell(address: int) -> ByAudience:
|
|
75
|
-
"""
|
|
77
|
+
def address_cell(address: int, *, conceal: bool = False) -> ByAudience:
|
|
78
|
+
"""An Acorn load/exec address as an audience-aware cell.
|
|
79
|
+
|
|
80
|
+
Humans read the ``0x``-prefixed hex form, trimmed of leading zeros
|
|
81
|
+
to a whole number of bytes (an even count of hex digits) with a
|
|
82
|
+
minimum of six — the width Acorn MOS uses for a DFS address, so a
|
|
83
|
+
table stays narrow without diverging from ``*EX`` / ``*INFO``. A
|
|
84
|
+
larger address (an ADFS 32-bit value) grows in whole bytes. Machine
|
|
85
|
+
formatters (JSON, TSV) get the raw integer, so a consumer never has
|
|
86
|
+
to parse a base back out of a string.
|
|
87
|
+
|
|
88
|
+
When *conceal* is set the human form is blank but the machine form
|
|
89
|
+
keeps the raw integer: a filetype-stamped file's load/exec hold an
|
|
90
|
+
encoded filetype and datestamp shown in their own columns, yet a
|
|
91
|
+
machine consumer still gets the faithful bytes.
|
|
92
|
+
"""
|
|
93
|
+
if conceal:
|
|
94
|
+
return ByAudience(machine=address, human="")
|
|
95
|
+
digits = max(6, len(f"{address:X}"))
|
|
96
|
+
width = digits + (digits & 1) # round up to a whole number of bytes
|
|
97
|
+
return ByAudience(machine=address, human=f"0x{address:0{width}X}")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def filetype_cell(filetype: int) -> ByAudience:
|
|
101
|
+
"""A RISC OS filetype as an audience-aware cell.
|
|
102
|
+
|
|
103
|
+
Humans read the registered name (or the ``&XXX`` hex form);
|
|
104
|
+
machine formatters get the raw 12-bit number.
|
|
105
|
+
"""
|
|
106
|
+
return ByAudience(machine=filetype, human=filetype_name(filetype))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def datestamp_cell(when: datetime, resolution: timedelta) -> str:
|
|
110
|
+
"""A datestamp as an ISO 8601 string at the filesystem's resolution.
|
|
76
111
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
112
|
+
A filesystem that records only a calendar day (AFS) yields a bare
|
|
113
|
+
date; one that records time of day (ADFS, centiseconds) yields a
|
|
114
|
+
full naive-local timestamp. The value is the same for humans and
|
|
115
|
+
machines, so a single string serves every formatter.
|
|
80
116
|
"""
|
|
81
|
-
|
|
117
|
+
if resolution >= timedelta(days=1):
|
|
118
|
+
return when.date().isoformat()
|
|
119
|
+
return when.isoformat(sep="T", timespec="milliseconds")
|
|
82
120
|
|
|
83
121
|
|
|
84
122
|
def kv_table(title: str, pairs: list[tuple[str, str, object]]):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oaknut-cli
|
|
3
|
-
Version: 12.
|
|
3
|
+
Version: 12.10.0
|
|
4
4
|
Summary: Shared CLI toolkit for the oaknut family: the contributed-command axis and report-rendering helpers a disc command needs, below the filesystem packages.
|
|
5
5
|
Author-email: Robert Smallshire <robert@smallshire.org.uk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,7 +23,7 @@ Requires-Dist: oaknut-exception>=10.0
|
|
|
23
23
|
Requires-Dist: oaknut-extension>=10.0
|
|
24
24
|
Requires-Dist: oaknut-file>=10.0
|
|
25
25
|
Requires-Dist: click>=8.1.7
|
|
26
|
-
Requires-Dist: asyoulikeit>=1.
|
|
26
|
+
Requires-Dist: asyoulikeit>=1.4.0
|
|
27
27
|
Dynamic: license-file
|
|
28
28
|
|
|
29
29
|
# oaknut-cli
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from asyoulikeit import ByAudience
|
|
6
|
-
from oaknut.cli import control_pictures, text_cell
|
|
6
|
+
from oaknut.cli import address_cell, control_pictures, text_cell
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TestControlPictures:
|
|
@@ -36,3 +36,27 @@ class TestTextCell:
|
|
|
36
36
|
# Machines keep the raw bytes; humans get control pictures.
|
|
37
37
|
assert cell.machine == "\x0cPascal\n\r"
|
|
38
38
|
assert cell.human == "␌Pascal␊␍"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestAddressCell:
|
|
42
|
+
def test_machine_keeps_raw_int(self):
|
|
43
|
+
cell = address_cell(0x1900)
|
|
44
|
+
assert isinstance(cell, ByAudience)
|
|
45
|
+
assert cell.machine == 0x1900
|
|
46
|
+
|
|
47
|
+
def test_human_keeps_0x_prefix(self):
|
|
48
|
+
assert address_cell(0x1900).human.startswith("0x")
|
|
49
|
+
|
|
50
|
+
def test_human_trimmed_to_minimum_six_hexits(self):
|
|
51
|
+
# Acorn MOS shows DFS addresses in six hex digits; leading zeros
|
|
52
|
+
# beyond that serve no purpose, so the narrowest form is six.
|
|
53
|
+
assert address_cell(0x1900).human == "0x001900"
|
|
54
|
+
assert address_cell(0x838F).human == "0x00838F"
|
|
55
|
+
assert address_cell(0x0).human == "0x000000"
|
|
56
|
+
|
|
57
|
+
def test_human_grows_in_whole_bytes(self):
|
|
58
|
+
# Above six hexits the width rounds up to an even number of hexits
|
|
59
|
+
# (a whole number of bytes), never an odd count.
|
|
60
|
+
assert address_cell(0xFFFFFF).human == "0xFFFFFF"
|
|
61
|
+
assert address_cell(0x1FF0000).human == "0x01FF0000"
|
|
62
|
+
assert address_cell(0xFFFF0E00).human == "0xFFFF0E00"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|