borg-space 2.3__tar.gz → 2.5__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.
- {borg_space-2.3 → borg_space-2.5}/PKG-INFO +35 -22
- {borg_space-2.3 → borg_space-2.5}/README.rst +34 -21
- {borg_space-2.3 → borg_space-2.5}/borg_space/config.py +67 -37
- {borg_space-2.3 → borg_space-2.5}/borg_space/main.py +12 -6
- {borg_space-2.3 → borg_space-2.5}/pyproject.toml +15 -2
- {borg_space-2.3 → borg_space-2.5}/LICENSE +0 -0
- {borg_space-2.3 → borg_space-2.5}/borg_space/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: borg_space
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5
|
|
4
4
|
Summary: Accessory for Emborg used to report and track the size of your Borg repositories
|
|
5
5
|
Keywords: emborg,borg,backups
|
|
6
6
|
Author-email: Ken Kundert <borg-space@nurdletech.com>
|
|
@@ -39,11 +39,12 @@ Borg-Space — Report and track the size of your Emborg repositories
|
|
|
39
39
|
:target: https://pypi.python.org/pypi/borg-space/
|
|
40
40
|
|
|
41
41
|
:Author: Ken Kundert
|
|
42
|
-
:Version: 2.
|
|
43
|
-
:Released:
|
|
42
|
+
:Version: 2.5
|
|
43
|
+
:Released: 2026-06-28
|
|
44
44
|
|
|
45
|
-
*Borg-Space* is an accessory for Emborg_. It reports on the
|
|
46
|
-
your *BorgBackup* repositories. You can get this information
|
|
45
|
+
*Borg-Space* is an accessory for Emborg_ and Assimilate_. It reports on the
|
|
46
|
+
space consumed by your *BorgBackup* repositories. You can get this information
|
|
47
|
+
using the
|
|
47
48
|
``emborg info`` command, but there are several reasons to prefer *Borg-Space*.
|
|
48
49
|
|
|
49
50
|
#. *Borg-Space* is capable of reporting on many repositories at once.
|
|
@@ -131,6 +132,7 @@ You can create a NestedText_ settings file to specify default behaviors and
|
|
|
131
132
|
define composite repositories. For example::
|
|
132
133
|
|
|
133
134
|
default repository: home
|
|
135
|
+
default path: ~{user}/.local/share/assimilate/{config}.latest.nt
|
|
134
136
|
report style: tree
|
|
135
137
|
compact format: {name}: {size:{fmt}}. Last back up: {last_create:ddd, MMM DD}. Last squeeze: {last_squeeze:ddd, MMM DD}.
|
|
136
138
|
table format: {host:<8} {user:<5} {config:<9} {size:<8.2b} {last_create:ddd, MMM DD}
|
|
@@ -146,7 +148,11 @@ define composite repositories. For example::
|
|
|
146
148
|
dev: root@dev~root
|
|
147
149
|
mail: root@mail~root
|
|
148
150
|
files: root@files~root
|
|
149
|
-
bastion:
|
|
151
|
+
bastion:
|
|
152
|
+
config: root
|
|
153
|
+
host: bastion
|
|
154
|
+
user: root
|
|
155
|
+
path: /root/.local/share/emborg/root.latest.nt
|
|
150
156
|
media: root@media~root
|
|
151
157
|
web: root@web~root
|
|
152
158
|
cluster: home@cluster
|
|
@@ -166,7 +172,17 @@ define composite repositories. For example::
|
|
|
166
172
|
children: home servers root
|
|
167
173
|
|
|
168
174
|
default repository:
|
|
169
|
-
The name of the repository to be used if none are given on the
|
|
175
|
+
The name (or names) of the repository to be used if none are given on the
|
|
176
|
+
command line.
|
|
177
|
+
|
|
178
|
+
default path:
|
|
179
|
+
The path to the *Emborg* or *Assimilate* generated latest.nt files. If not
|
|
180
|
+
give, it defaults to::
|
|
181
|
+
|
|
182
|
+
~{user}/.local/share/emborg/{config}.latest.nt
|
|
183
|
+
|
|
184
|
+
``{config}`` and ``{user}`` are placeholders that are replaced by the
|
|
185
|
+
corresponding component of the repository specification.
|
|
170
186
|
|
|
171
187
|
report style:
|
|
172
188
|
The report style to be used if none is specified on the command line.
|
|
@@ -262,6 +278,7 @@ repositories:
|
|
|
262
278
|
config: home
|
|
263
279
|
host: host
|
|
264
280
|
user: user
|
|
281
|
+
path: ~user/.local/share/emborg/home.latest.nt
|
|
265
282
|
|
|
266
283
|
repositiories:
|
|
267
284
|
all: home@host~user work@host~user
|
|
@@ -280,6 +297,7 @@ repositories:
|
|
|
280
297
|
config: home
|
|
281
298
|
host: host
|
|
282
299
|
user: user
|
|
300
|
+
path: ~user/.local/share/emborg/home.latest.nt
|
|
283
301
|
-
|
|
284
302
|
config: work
|
|
285
303
|
host: host
|
|
@@ -342,30 +360,25 @@ scaled nicely on the same graph::
|
|
|
342
360
|
Installation
|
|
343
361
|
------------
|
|
344
362
|
|
|
345
|
-
*Borg-Space* requires *Emborg* version 1.37 or newer
|
|
363
|
+
*Borg-Space* requires *Emborg* version 1.37 or newer or *Assimilate*.
|
|
346
364
|
|
|
347
365
|
Install with::
|
|
348
366
|
|
|
349
367
|
> pip3 install borg-space
|
|
350
368
|
|
|
351
369
|
|
|
352
|
-
Borg 2
|
|
353
|
-
|
|
370
|
+
Assimilate and Borg 2
|
|
371
|
+
---------------------
|
|
354
372
|
|
|
355
373
|
Borg_ 2 will be released soon, and with it will come Assimilate_, the next
|
|
356
374
|
generation of Emborg_. *Assimilate* is intended to be used with *Borg 2.0* and
|
|
357
|
-
beyond while *Emborg* would be used with older versions of *Borg*.
|
|
358
|
-
*
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
*
|
|
364
|
-
to use the *latest.nt* file produced by *Assimilate*::
|
|
365
|
-
|
|
366
|
-
cd ~/.local/share/emborg
|
|
367
|
-
rm home.latest.nt
|
|
368
|
-
ln -s ../assimilate/home.latest.nt .
|
|
375
|
+
beyond while *Emborg* would be used with older versions of *Borg*. To use
|
|
376
|
+
*Assimilate* you should set the *default path* accordingly. To support both
|
|
377
|
+
*Emborg* and *Assimilate* simultaneously, you should set *default path* for one
|
|
378
|
+
and then use *path* overrides for individual repositories.
|
|
379
|
+
|
|
380
|
+
*Assimilate* only saves the space used by the repository when running
|
|
381
|
+
a *compact* command and only if the *get_repo_size* is set to ``'yes``.
|
|
369
382
|
|
|
370
383
|
.. _assimilate: https://assimilate.readthedocs.io
|
|
371
384
|
.. _borg: https://borgbackup.readthedocs.io
|
|
@@ -11,11 +11,12 @@ Borg-Space — Report and track the size of your Emborg repositories
|
|
|
11
11
|
:target: https://pypi.python.org/pypi/borg-space/
|
|
12
12
|
|
|
13
13
|
:Author: Ken Kundert
|
|
14
|
-
:Version: 2.
|
|
15
|
-
:Released:
|
|
14
|
+
:Version: 2.5
|
|
15
|
+
:Released: 2026-06-28
|
|
16
16
|
|
|
17
|
-
*Borg-Space* is an accessory for Emborg_. It reports on the
|
|
18
|
-
your *BorgBackup* repositories. You can get this information
|
|
17
|
+
*Borg-Space* is an accessory for Emborg_ and Assimilate_. It reports on the
|
|
18
|
+
space consumed by your *BorgBackup* repositories. You can get this information
|
|
19
|
+
using the
|
|
19
20
|
``emborg info`` command, but there are several reasons to prefer *Borg-Space*.
|
|
20
21
|
|
|
21
22
|
#. *Borg-Space* is capable of reporting on many repositories at once.
|
|
@@ -103,6 +104,7 @@ You can create a NestedText_ settings file to specify default behaviors and
|
|
|
103
104
|
define composite repositories. For example::
|
|
104
105
|
|
|
105
106
|
default repository: home
|
|
107
|
+
default path: ~{user}/.local/share/assimilate/{config}.latest.nt
|
|
106
108
|
report style: tree
|
|
107
109
|
compact format: {name}: {size:{fmt}}. Last back up: {last_create:ddd, MMM DD}. Last squeeze: {last_squeeze:ddd, MMM DD}.
|
|
108
110
|
table format: {host:<8} {user:<5} {config:<9} {size:<8.2b} {last_create:ddd, MMM DD}
|
|
@@ -118,7 +120,11 @@ define composite repositories. For example::
|
|
|
118
120
|
dev: root@dev~root
|
|
119
121
|
mail: root@mail~root
|
|
120
122
|
files: root@files~root
|
|
121
|
-
bastion:
|
|
123
|
+
bastion:
|
|
124
|
+
config: root
|
|
125
|
+
host: bastion
|
|
126
|
+
user: root
|
|
127
|
+
path: /root/.local/share/emborg/root.latest.nt
|
|
122
128
|
media: root@media~root
|
|
123
129
|
web: root@web~root
|
|
124
130
|
cluster: home@cluster
|
|
@@ -138,7 +144,17 @@ define composite repositories. For example::
|
|
|
138
144
|
children: home servers root
|
|
139
145
|
|
|
140
146
|
default repository:
|
|
141
|
-
The name of the repository to be used if none are given on the
|
|
147
|
+
The name (or names) of the repository to be used if none are given on the
|
|
148
|
+
command line.
|
|
149
|
+
|
|
150
|
+
default path:
|
|
151
|
+
The path to the *Emborg* or *Assimilate* generated latest.nt files. If not
|
|
152
|
+
give, it defaults to::
|
|
153
|
+
|
|
154
|
+
~{user}/.local/share/emborg/{config}.latest.nt
|
|
155
|
+
|
|
156
|
+
``{config}`` and ``{user}`` are placeholders that are replaced by the
|
|
157
|
+
corresponding component of the repository specification.
|
|
142
158
|
|
|
143
159
|
report style:
|
|
144
160
|
The report style to be used if none is specified on the command line.
|
|
@@ -234,6 +250,7 @@ repositories:
|
|
|
234
250
|
config: home
|
|
235
251
|
host: host
|
|
236
252
|
user: user
|
|
253
|
+
path: ~user/.local/share/emborg/home.latest.nt
|
|
237
254
|
|
|
238
255
|
repositiories:
|
|
239
256
|
all: home@host~user work@host~user
|
|
@@ -252,6 +269,7 @@ repositories:
|
|
|
252
269
|
config: home
|
|
253
270
|
host: host
|
|
254
271
|
user: user
|
|
272
|
+
path: ~user/.local/share/emborg/home.latest.nt
|
|
255
273
|
-
|
|
256
274
|
config: work
|
|
257
275
|
host: host
|
|
@@ -314,30 +332,25 @@ scaled nicely on the same graph::
|
|
|
314
332
|
Installation
|
|
315
333
|
------------
|
|
316
334
|
|
|
317
|
-
*Borg-Space* requires *Emborg* version 1.37 or newer
|
|
335
|
+
*Borg-Space* requires *Emborg* version 1.37 or newer or *Assimilate*.
|
|
318
336
|
|
|
319
337
|
Install with::
|
|
320
338
|
|
|
321
339
|
> pip3 install borg-space
|
|
322
340
|
|
|
323
341
|
|
|
324
|
-
Borg 2
|
|
325
|
-
|
|
342
|
+
Assimilate and Borg 2
|
|
343
|
+
---------------------
|
|
326
344
|
|
|
327
345
|
Borg_ 2 will be released soon, and with it will come Assimilate_, the next
|
|
328
346
|
generation of Emborg_. *Assimilate* is intended to be used with *Borg 2.0* and
|
|
329
|
-
beyond while *Emborg* would be used with older versions of *Borg*.
|
|
330
|
-
*
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
*
|
|
336
|
-
to use the *latest.nt* file produced by *Assimilate*::
|
|
337
|
-
|
|
338
|
-
cd ~/.local/share/emborg
|
|
339
|
-
rm home.latest.nt
|
|
340
|
-
ln -s ../assimilate/home.latest.nt .
|
|
347
|
+
beyond while *Emborg* would be used with older versions of *Borg*. To use
|
|
348
|
+
*Assimilate* you should set the *default path* accordingly. To support both
|
|
349
|
+
*Emborg* and *Assimilate* simultaneously, you should set *default path* for one
|
|
350
|
+
and then use *path* overrides for individual repositories.
|
|
351
|
+
|
|
352
|
+
*Assimilate* only saves the space used by the repository when running
|
|
353
|
+
a *compact* command and only if the *get_repo_size* is set to ``'yes``.
|
|
341
354
|
|
|
342
355
|
.. _assimilate: https://assimilate.readthedocs.io
|
|
343
356
|
.. _borg: https://borgbackup.readthedocs.io
|
|
@@ -32,7 +32,6 @@ if new_home: # pragma: no cover
|
|
|
32
32
|
|
|
33
33
|
# GLOBALS {{{1
|
|
34
34
|
set_prefs(use_inform=True)
|
|
35
|
-
settings_file = to_path(user_config_dir('borg-space')) / 'settings.nt'
|
|
36
35
|
voluptuous_error_msg_mappings = {
|
|
37
36
|
"extra keys not allowed": ("unknown key", "key"),
|
|
38
37
|
"expected a dictionary": ("expected key:value pair", "value"),
|
|
@@ -41,24 +40,43 @@ voluptuous_key_prefix = "key contains"
|
|
|
41
40
|
hostname = socket.gethostname().split('.')[0]
|
|
42
41
|
# version of the hostname (the hostname without any domain name)
|
|
43
42
|
username = pwd.getpwuid(os.getuid()).pw_name
|
|
43
|
+
program_name = 'borg-space'
|
|
44
|
+
if 'XDG_CONFIG_HOME' in os.environ:
|
|
45
|
+
config_dir = os.sep.join([os.environ['XDG_CONFIG_HOME'], program_name])
|
|
46
|
+
else:
|
|
47
|
+
config_dir = user_config_dir(program_name)
|
|
48
|
+
settings_file = to_path(config_dir) / 'settings.nt'
|
|
44
49
|
|
|
45
50
|
|
|
46
51
|
# REPOSITORY {{{1
|
|
47
52
|
# Repository class {{{2
|
|
48
53
|
class Repository:
|
|
49
54
|
def __init__(self, spec, name=None):
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
if is_str(spec):
|
|
56
|
+
prefix, _, user = spec.partition('~')
|
|
57
|
+
config, _, host = prefix.partition('@')
|
|
58
|
+
path = None
|
|
59
|
+
else:
|
|
60
|
+
user = spec.get('user')
|
|
61
|
+
config = spec.get('config')
|
|
62
|
+
host = spec.get('host')
|
|
63
|
+
path = spec.get('path')
|
|
64
|
+
spec = spec.get('spec')
|
|
65
|
+
|
|
52
66
|
if not config:
|
|
53
67
|
raise Error("spec is missing Emborg config name.", culprit=spec)
|
|
54
68
|
if not name:
|
|
55
69
|
name = spec
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
try:
|
|
72
|
+
self.spec = spec
|
|
73
|
+
self.name = name
|
|
74
|
+
self.config = a_name(config)
|
|
75
|
+
self.host = a_name(host) or hostname
|
|
76
|
+
self.user = a_name(user) or username
|
|
77
|
+
self.path = path
|
|
78
|
+
except Invalid as e:
|
|
79
|
+
raise Error(e, culprit=spec)
|
|
62
80
|
self.latest = None
|
|
63
81
|
|
|
64
82
|
def __str__(self):
|
|
@@ -87,14 +105,21 @@ class Repository:
|
|
|
87
105
|
user = self.user,
|
|
88
106
|
full_spec = str(self)
|
|
89
107
|
)
|
|
90
|
-
|
|
108
|
+
if self.latest:
|
|
109
|
+
info.update(self.latest)
|
|
91
110
|
return info
|
|
92
111
|
|
|
93
112
|
def get_path(self):
|
|
94
113
|
user = self.user if self.user else getpass.getuser()
|
|
95
114
|
config = self.config
|
|
96
115
|
assert config
|
|
97
|
-
path =
|
|
116
|
+
path = self.path
|
|
117
|
+
if not path:
|
|
118
|
+
path = settings.get(
|
|
119
|
+
'default_path',
|
|
120
|
+
"~{user}/.local/share/emborg/{config}.latest.nt"
|
|
121
|
+
)
|
|
122
|
+
path = path.format(user=user, config=config)
|
|
98
123
|
return (self.host, path)
|
|
99
124
|
|
|
100
125
|
def get_latest(self):
|
|
@@ -109,7 +134,7 @@ class Repository:
|
|
|
109
134
|
try:
|
|
110
135
|
content = to_path(path).read_text()
|
|
111
136
|
except FileNotFoundError:
|
|
112
|
-
raise Error('
|
|
137
|
+
raise Error(f'repository not found: {path}', culprit=str(self))
|
|
113
138
|
raw_data = nt.loads(content)
|
|
114
139
|
self.latest = data = {}
|
|
115
140
|
if 'repository size' in raw_data:
|
|
@@ -128,27 +153,27 @@ class Repository:
|
|
|
128
153
|
def get_repos(spec):
|
|
129
154
|
if not spec:
|
|
130
155
|
spec = settings.get('default_repository')
|
|
131
|
-
if not spec:
|
|
132
|
-
raise Error('there is no default repository.')
|
|
133
156
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
except (TypeError, KeyError):
|
|
137
|
-
# not found in repositories specified in settings file.
|
|
138
|
-
# see if it exists on local machine
|
|
139
|
-
children = [Repository(spec)]
|
|
140
|
-
|
|
141
|
-
results = {}
|
|
142
|
-
for child in children:
|
|
143
|
-
host, path = child.get_path()
|
|
144
|
-
name = str(child)
|
|
157
|
+
specs = spec.split()
|
|
158
|
+
for spec in specs:
|
|
145
159
|
try:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
160
|
+
children = repositories[spec]
|
|
161
|
+
except (TypeError, KeyError):
|
|
162
|
+
# not found in repositories specified in settings file.
|
|
163
|
+
# see if it exists on local machine
|
|
164
|
+
children = [Repository(spec)]
|
|
165
|
+
|
|
166
|
+
results = {}
|
|
167
|
+
for child in children:
|
|
168
|
+
# host, path = child.get_path()
|
|
169
|
+
name = str(child)
|
|
170
|
+
try:
|
|
171
|
+
child.get_latest()
|
|
172
|
+
results[name] = child
|
|
173
|
+
except Error as e:
|
|
174
|
+
e.report(culprit=name)
|
|
175
|
+
except OSError as e:
|
|
176
|
+
error(os_error(e), culprit=name)
|
|
152
177
|
return results
|
|
153
178
|
|
|
154
179
|
|
|
@@ -168,7 +193,7 @@ def to_list(args):
|
|
|
168
193
|
if is_str(args):
|
|
169
194
|
args = args.split()
|
|
170
195
|
if is_mapping(args):
|
|
171
|
-
raise Invalid(
|
|
196
|
+
raise Invalid("expected a list or string")
|
|
172
197
|
return args
|
|
173
198
|
|
|
174
199
|
# a_name() {{{2
|
|
@@ -200,7 +225,7 @@ def a_spec(arg):
|
|
|
200
225
|
if is_str(arg):
|
|
201
226
|
return arg
|
|
202
227
|
if is_mapping(arg):
|
|
203
|
-
unknown_keys = arg.keys() - set(['config', 'host', 'user'])
|
|
228
|
+
unknown_keys = arg.keys() - set(['config', 'host', 'user', 'path'])
|
|
204
229
|
if unknown_keys:
|
|
205
230
|
raise Invalid(f"unknown {plural(unknown_keys):key}: {conjoin(unknown_keys)}.")
|
|
206
231
|
if 'config' not in arg:
|
|
@@ -210,7 +235,8 @@ def a_spec(arg):
|
|
|
210
235
|
spec = f"{spec}@{arg.get('host')}"
|
|
211
236
|
if arg.get('user'):
|
|
212
237
|
spec = f"{spec}~{arg.get('user')}"
|
|
213
|
-
|
|
238
|
+
arg['spec'] = spec
|
|
239
|
+
return arg
|
|
214
240
|
raise Invalid("expected a specification")
|
|
215
241
|
|
|
216
242
|
# to_specs() {{{2
|
|
@@ -218,7 +244,7 @@ def to_specs(arg):
|
|
|
218
244
|
if is_str(arg):
|
|
219
245
|
return [a_spec(r) for r in arg.split()]
|
|
220
246
|
if is_mapping(arg):
|
|
221
|
-
unknown_keys = arg.keys() - set(['config', 'host', 'user'])
|
|
247
|
+
unknown_keys = arg.keys() - set(['config', 'host', 'user', 'path'])
|
|
222
248
|
if unknown_keys:
|
|
223
249
|
raise Invalid(f"unknown {plural(unknown_keys):key}: {conjoin(unknown_keys)}.")
|
|
224
250
|
return [a_spec(arg)]
|
|
@@ -230,6 +256,7 @@ def to_specs(arg):
|
|
|
230
256
|
validate_settings = Schema({
|
|
231
257
|
'repositories': {key_as_name: to_specs},
|
|
232
258
|
'default_repository': str,
|
|
259
|
+
'default_path': str,
|
|
233
260
|
'report_style': str,
|
|
234
261
|
'compact_format': str,
|
|
235
262
|
'table_format': str,
|
|
@@ -264,9 +291,12 @@ try:
|
|
|
264
291
|
repositories[name] = []
|
|
265
292
|
alias = name if len(specs) <= 1 else None
|
|
266
293
|
for spec in specs:
|
|
267
|
-
|
|
294
|
+
specname = spec
|
|
295
|
+
if is_mapping(spec):
|
|
296
|
+
specname = spec['spec']
|
|
297
|
+
if specname in repositories and specname != name:
|
|
268
298
|
# this is a known (previously defined) repository
|
|
269
|
-
repositories[name].extend(repositories[
|
|
299
|
+
repositories[name].extend(repositories[specname])
|
|
270
300
|
else:
|
|
271
301
|
repositories[name].append(Repository(spec, alias))
|
|
272
302
|
else:
|
|
@@ -292,7 +322,7 @@ except MultipleInvalid as e: # report schema violations
|
|
|
292
322
|
flag = 'key'
|
|
293
323
|
loc = keymap.get(tuple(err.path))
|
|
294
324
|
codicil = loc.as_line(flag) if loc else None
|
|
295
|
-
keys = nt.
|
|
325
|
+
keys = nt.get_keys(err.path, keymap=keymap, sep=', ')
|
|
296
326
|
error(
|
|
297
327
|
full_stop(msg),
|
|
298
328
|
culprit = (settings_file, keys),
|
|
@@ -31,7 +31,7 @@ Settings are held in ~/.config/borg-space/settings.nt.
|
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
# imports {{{1
|
|
34
|
-
from .config import settings, get_repos
|
|
34
|
+
from .config import settings, get_repos, program_name
|
|
35
35
|
import arrow
|
|
36
36
|
from appdirs import user_data_dir
|
|
37
37
|
from docopt import docopt
|
|
@@ -45,14 +45,18 @@ import matplotlib.pyplot as plt
|
|
|
45
45
|
from matplotlib.dates import AutoDateFormatter, AutoDateLocator
|
|
46
46
|
from matplotlib.ticker import FuncFormatter
|
|
47
47
|
# from labellines import labelLines
|
|
48
|
+
import os
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
# globals {{{1
|
|
51
|
-
|
|
52
|
+
if 'XDG_DATA_HOME' in os.environ:
|
|
53
|
+
data_dir = Path(os.sep.join([os.environ['XDG_DATA_HOME'], program_name]))
|
|
54
|
+
else:
|
|
55
|
+
data_dir = Path(user_data_dir(program_name))
|
|
52
56
|
now = str(arrow.now())
|
|
53
57
|
Quantity.set_prefs(prec='full')
|
|
54
|
-
__version__ = "2.
|
|
55
|
-
__released__ = "
|
|
58
|
+
__version__ = "2.5"
|
|
59
|
+
__released__ = "2026-06-28"
|
|
56
60
|
date_format = settings.get('date_format', 'D MMMM YYYY')
|
|
57
61
|
size_format = settings.get('size_format', '.2b')
|
|
58
62
|
nestedtext_size_format = settings.get('nestedtext_size_format', size_format)
|
|
@@ -65,6 +69,8 @@ not_available = "⟪not available⟫"
|
|
|
65
69
|
# collect_repos() {{{1
|
|
66
70
|
def collect_repos(requests, record_size):
|
|
67
71
|
repos = {}
|
|
72
|
+
if not requests:
|
|
73
|
+
raise Error('there is no default repository.')
|
|
68
74
|
for request in requests:
|
|
69
75
|
new_repos = get_repos(request)
|
|
70
76
|
repos.update(new_repos)
|
|
@@ -74,7 +80,7 @@ def collect_repos(requests, record_size):
|
|
|
74
80
|
for name, repo in repos.items():
|
|
75
81
|
|
|
76
82
|
# read previously recorded sizes
|
|
77
|
-
data_path =
|
|
83
|
+
data_path = data_dir / f'{name}.nt'
|
|
78
84
|
try:
|
|
79
85
|
data = nt.load(data_path, top=dict)
|
|
80
86
|
except FileNotFoundError:
|
|
@@ -330,7 +336,7 @@ def main():
|
|
|
330
336
|
|
|
331
337
|
requests = cmdline['<spec>']
|
|
332
338
|
if not requests:
|
|
333
|
-
requests =
|
|
339
|
+
requests = settings.get('default_repository', '').split()
|
|
334
340
|
|
|
335
341
|
try:
|
|
336
342
|
repos = collect_repos(requests, cmdline['--record'])
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "borg_space"
|
|
3
3
|
dist-name = "borg-space"
|
|
4
|
-
version = "2.
|
|
4
|
+
version = "2.5"
|
|
5
5
|
description = "Accessory for Emborg used to report and track the size of your Borg repositories"
|
|
6
6
|
readme = "README.rst"
|
|
7
7
|
requires-python = ">=3.6"
|
|
@@ -38,5 +38,18 @@ changelog = "https://github.com/KenKundert/ntlog/blob/master/CHANGELOG.rst"
|
|
|
38
38
|
borg-space = "borg_space.main:main"
|
|
39
39
|
|
|
40
40
|
[build-system]
|
|
41
|
-
requires = ["flit_core >=2
|
|
41
|
+
requires = ["flit_core >=2"]
|
|
42
42
|
build-backend = "flit_core.buildapi"
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
addopts = "--tb=short"
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
exclude = [".tox", "doc", "Diffs"]
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = ["F"]
|
|
52
|
+
ignore = []
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint.per-file-ignores]
|
|
55
|
+
"borg_space/__init__.py" = ["F401"]
|
|
File without changes
|
|
File without changes
|