borgapi 0.6.1__py3-none-any.whl → 0.7.1__py3-none-any.whl
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.
- borgapi/__init__.py +44 -4
- borgapi/borgapi.py +616 -313
- borgapi/capture.py +416 -0
- borgapi/helpers.py +26 -0
- borgapi/options.py +137 -68
- {borgapi-0.6.1.dist-info → borgapi-0.7.1.dist-info}/METADATA +68 -44
- borgapi-0.7.1.dist-info/RECORD +10 -0
- {borgapi-0.6.1.dist-info → borgapi-0.7.1.dist-info}/WHEEL +1 -1
- {borgapi-0.6.1.dist-info → borgapi-0.7.1.dist-info}/top_level.txt +0 -1
- borgapi-0.6.1.dist-info/RECORD +0 -27
- test/__init__.py +0 -0
- test/borgapi/__init__.py +0 -0
- test/borgapi/test_01_borgapi.py +0 -226
- test/borgapi/test_02_init.py +0 -78
- test/borgapi/test_03_create.py +0 -136
- test/borgapi/test_04_extract.py +0 -52
- test/borgapi/test_05_rename.py +0 -32
- test/borgapi/test_06_list.py +0 -59
- test/borgapi/test_07_diff.py +0 -54
- test/borgapi/test_08_delete.py +0 -68
- test/borgapi/test_09_prune.py +0 -48
- test/borgapi/test_10_info.py +0 -50
- test/borgapi/test_11_mount.py +0 -50
- test/borgapi/test_12_key.py +0 -81
- test/borgapi/test_13_export_tar.py +0 -38
- test/borgapi/test_14_config.py +0 -42
- test/borgapi/test_15_benchmark_crud.py +0 -24
- test/borgapi/test_16_compact.py +0 -33
- test/test_00_options.py +0 -70
- {borgapi-0.6.1.dist-info → borgapi-0.7.1.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: borgapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Wrapper for borgbackup to easily use in code
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
Author-email: Sean Slater <seanslater@whatno.io>
|
|
6
|
+
Project-URL: homepage, https://github.com/spslater/borgapi
|
|
7
|
+
Project-URL: documentation, https://github.com/spslater/borgapi/blob/master/README.md
|
|
8
|
+
Project-URL: repository, https://github.com/spslater/borgapi.git
|
|
9
|
+
Project-URL: issues, https://github.com/spslater/borgapi/issues
|
|
10
|
+
Project-URL: changelog, https://github.com/spslater/borgapi/blob/master/CHANGELOG.md
|
|
11
|
+
Keywords: borgbackup,backup,api
|
|
10
12
|
Classifier: Development Status :: 4 - Beta
|
|
11
13
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.9
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
19
|
Classifier: Operating System :: OS Independent
|
|
17
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
18
20
|
Classifier: Topic :: Utilities
|
|
19
21
|
Classifier: Topic :: System :: Archiving :: Backup
|
|
20
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.9
|
|
21
23
|
Description-Content-Type: text/markdown
|
|
22
24
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: borgbackup[llfuse]
|
|
24
|
-
Requires-Dist: python-dotenv
|
|
25
|
+
Requires-Dist: borgbackup[llfuse]~=1.4.0
|
|
26
|
+
Requires-Dist: python-dotenv~=1.0.0
|
|
27
|
+
Dynamic: license-file
|
|
25
28
|
|
|
26
29
|
# BorgAPI
|
|
27
30
|
|
|
@@ -36,10 +39,10 @@ pip install borgapi
|
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
Requires:
|
|
39
|
-
* `borgbackup`: 1.
|
|
40
|
-
* `python-dotenv`: 1.0.
|
|
42
|
+
* `borgbackup`: 1.4.0
|
|
43
|
+
* `python-dotenv`: 1.0.1
|
|
41
44
|
|
|
42
|
-
Supports Python 3.
|
|
45
|
+
Supports Python 3.9 to 3.13
|
|
43
46
|
|
|
44
47
|
## Usage
|
|
45
48
|
```python
|
|
@@ -48,12 +51,12 @@ import borgapi
|
|
|
48
51
|
api = borgapi.BorgAPI(defaults={}, options={})
|
|
49
52
|
|
|
50
53
|
# Initalize new repository
|
|
51
|
-
api.init("
|
|
54
|
+
api.init("foo/bar", make_parent_dirs=True)
|
|
52
55
|
|
|
53
56
|
# Create backup
|
|
54
|
-
result = api.create("
|
|
57
|
+
result = api.create("foo/bar::backup", "/home", "/mnt/baz", json=True)
|
|
55
58
|
print(result['archive']["name"]) # backup
|
|
56
|
-
print(result["repository"]["location"]) #
|
|
59
|
+
print(result["repository"]["location"]) # foo/bar
|
|
57
60
|
```
|
|
58
61
|
|
|
59
62
|
### BorgAPI Init arguments
|
|
@@ -62,7 +65,8 @@ class BorgAPI(
|
|
|
62
65
|
defaults: dict = None,
|
|
63
66
|
options: dict = None,
|
|
64
67
|
log_level: str = "warning",
|
|
65
|
-
log_json: bool = False
|
|
68
|
+
log_json: bool = False,
|
|
69
|
+
environ: dict = None,
|
|
66
70
|
)
|
|
67
71
|
```
|
|
68
72
|
* __defaults__: dictionary that has command names as keys and value that is a dict of
|
|
@@ -96,12 +100,35 @@ class BorgAPI(
|
|
|
96
100
|
level as and keyword argument
|
|
97
101
|
* __log_json__: log lines written by logger are formatted as json lines, passed into the
|
|
98
102
|
logging setup
|
|
103
|
+
* __environ__: dictionary that contains environmental variables that should be set before running
|
|
104
|
+
any commands. Useful for setting the passphrase or passcommand for the repository or other
|
|
105
|
+
settings like that. See [Environment Variables](#Setting-Environment-Variables) section for
|
|
106
|
+
how to set environmental variables after initalization or what the defaults are.
|
|
107
|
+
```python
|
|
108
|
+
{
|
|
109
|
+
"BORG_CHECK_I_KNOW_WHAT_I_AM_DOING": "YES",
|
|
110
|
+
"BORG_PASSCOMMAND": "cat ~/.borg/password",
|
|
111
|
+
}
|
|
112
|
+
```
|
|
99
113
|
|
|
100
114
|
### Setting Environment Variables
|
|
101
115
|
You are able to manage the environment variables used by borg to be able to use different settings
|
|
102
116
|
for different repositories.
|
|
103
117
|
|
|
104
|
-
|
|
118
|
+
When initialzing the `BorgAPI` object, you can include a dictionary with the `environ` argument.
|
|
119
|
+
|
|
120
|
+
The following are the defaults that BorgAPI will always load so that user input does not hold up
|
|
121
|
+
the app from progressing.
|
|
122
|
+
```ini
|
|
123
|
+
BORG_EXIT_CODES=modern,
|
|
124
|
+
BORG_PASSPHRASE="",
|
|
125
|
+
BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no,
|
|
126
|
+
BORG_RELOCATED_REPO_ACCESS_IS_OK=no,
|
|
127
|
+
BORG_CHECK_I_KNOW_WHAT_I_AM_DOING=NO,
|
|
128
|
+
BORG_DELETE_I_KNOW_WHAT_I_AM_DOING=NO,
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
There are 3 ways you can set the variables after initialization:
|
|
105
132
|
1. `filename`: Path to a file that contains the variables and their values. See the
|
|
106
133
|
[python-dotenv README](https://github.com/theskumar/python-dotenv/blob/master/README.md#file-format)
|
|
107
134
|
for more information.
|
|
@@ -124,14 +151,6 @@ will be used, which is searching for a ".env" file somewhere above in the curren
|
|
|
124
151
|
[Environment Variables](https://borgbackup.readthedocs.io/en/stable/usage/general.html#environment-variables)
|
|
125
152
|
used by `borgbackup`.
|
|
126
153
|
|
|
127
|
-
#### IMPORTANT
|
|
128
|
-
For commands that borg requires a confirmation on if no environment variable is given, the api will
|
|
129
|
-
become stuck as it waits for a `yes` or `no` answer.
|
|
130
|
-
* BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK
|
|
131
|
-
* BORG_RELOCATED_REPO_ACCESS_IS_OK
|
|
132
|
-
* BORG_CHECK_I_KNOW_WHAT_I_AM_DOING
|
|
133
|
-
* BORG_DELETE_I_KNOW_WHAT_I_AM_DOING
|
|
134
|
-
|
|
135
154
|
### Removing Environment Variables
|
|
136
155
|
If you want to unset a variable so it doesn't get used for another command you can use the
|
|
137
156
|
`unset_environ` method. It'll remove any variables passed in from the current environment.
|
|
@@ -158,7 +177,7 @@ So the `--storage-quota` argument in `init` gets turned into the keyword argumen
|
|
|
158
177
|
|
|
159
178
|
```python
|
|
160
179
|
api.init(
|
|
161
|
-
repository="
|
|
180
|
+
repository="foor/bar",
|
|
162
181
|
encryption="repokey",
|
|
163
182
|
append_only=True,
|
|
164
183
|
storage_quota="5G",
|
|
@@ -175,9 +194,9 @@ diff_args = {
|
|
|
175
194
|
}
|
|
176
195
|
|
|
177
196
|
api.diff(
|
|
178
|
-
"
|
|
197
|
+
"foo/bar::tuesday",
|
|
179
198
|
"friday",
|
|
180
|
-
"
|
|
199
|
+
"foo/bar",
|
|
181
200
|
"/baz",
|
|
182
201
|
**diff_args,
|
|
183
202
|
)
|
|
@@ -197,20 +216,18 @@ api.diff(
|
|
|
197
216
|
* info
|
|
198
217
|
* mount
|
|
199
218
|
* umount
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
219
|
+
* key change-passphrase (key_change_passphrase)
|
|
220
|
+
* key export (key_export)
|
|
221
|
+
* key import (key_import)
|
|
203
222
|
* upgrade
|
|
204
|
-
*
|
|
223
|
+
* recreate
|
|
224
|
+
* immport-tar (immport_tar)
|
|
225
|
+
* export-tar (export_tar)
|
|
205
226
|
* serve
|
|
206
227
|
* config
|
|
207
|
-
* with-lock
|
|
208
|
-
* break-lock
|
|
209
|
-
* benchmark crud
|
|
210
|
-
|
|
211
|
-
### Unavailable Borg Commands
|
|
212
|
-
* recreate
|
|
213
|
-
* Since this is an experimental feature there are no current plans to implament this.
|
|
228
|
+
* with-lock (with_lock)
|
|
229
|
+
* break-lock (break_lock)
|
|
230
|
+
* benchmark crud (benchmark_crud)
|
|
214
231
|
|
|
215
232
|
### Command Quirks
|
|
216
233
|
Things that were changed from the way the default borg commands work to make things a bit
|
|
@@ -235,7 +252,8 @@ which outputs the stats info as a json object, it gets written to stdout.
|
|
|
235
252
|
|
|
236
253
|
If either `json` or `log_json` is set, it'll try to convert the tuple output to json.
|
|
237
254
|
If it is unable and there is output that is captured it'll return the plaintext value.
|
|
238
|
-
If no output is captured, it returns `None
|
|
255
|
+
If no output is captured, it returns `None` if expecting a string or `{}` (an empty
|
|
256
|
+
dictionary) if expection some kind of JSON output.
|
|
239
257
|
|
|
240
258
|
If multiple outputs are requested at the same time (like `--stats` and `--list`) the command
|
|
241
259
|
will return a dictionary with aptly named keys (`--list` key is "list"). If only one output
|
|
@@ -267,6 +285,12 @@ Commands not listed return no output (None)
|
|
|
267
285
|
- info: `--info`
|
|
268
286
|
- info
|
|
269
287
|
- always returns bare value
|
|
288
|
+
- recreate:
|
|
289
|
+
- list: `--list`, `--log-json`
|
|
290
|
+
- stats: `--stats`
|
|
291
|
+
- import tar
|
|
292
|
+
- list: `--list`
|
|
293
|
+
- stats: `--stats`, `--json`
|
|
270
294
|
- export tar
|
|
271
295
|
- list: `--list`, `--log-json`
|
|
272
296
|
- tar: filename == "-"
|
|
@@ -277,7 +301,7 @@ Commands not listed return no output (None)
|
|
|
277
301
|
- always returns bare value
|
|
278
302
|
|
|
279
303
|
## Roadmap
|
|
280
|
-
- Start work on Borg's beta branch
|
|
304
|
+
- Start work on Borg's beta branch again and keeping up with those
|
|
281
305
|
|
|
282
306
|
## Links
|
|
283
307
|
* [PyPi Project](https://pypi.org/project/borgapi)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
borgapi/__init__.py,sha256=UksYdsjxl0cESQ_VZ0ye64JpZtQzlrnU5UiUiqxpLRc,1469
|
|
2
|
+
borgapi/borgapi.py,sha256=Wxv4bupAsOn2h5h3NYjGP65CdX1Gl47MbtHaLckCrg0,52079
|
|
3
|
+
borgapi/capture.py,sha256=bYnvo4JvjAkYVRFYnf1Mn-yeVkmBvmwL0m2obCb8TUk,12533
|
|
4
|
+
borgapi/helpers.py,sha256=wI41AL1VjQHwAJeqXvY94jZS5SrQVvGM0Pf2o3d2RoM,710
|
|
5
|
+
borgapi/options.py,sha256=tVD30frgYnUS9GmJbAyxuP_DdsjUn8_ImZV39VE7JHo,31835
|
|
6
|
+
borgapi-0.7.1.dist-info/licenses/LICENSE,sha256=g7PjdvkUMJa85GMb6zUCyW8aX_yk5Ym1x7if5W5ULPI,1050
|
|
7
|
+
borgapi-0.7.1.dist-info/METADATA,sha256=T6WyITECYLzD49XsJvflnZFFz7AjpyFNnaWDAdqMZ2c,10204
|
|
8
|
+
borgapi-0.7.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
+
borgapi-0.7.1.dist-info/top_level.txt,sha256=Ena1GrMubckEQBF7NtgxYXqFP_9for5BS7xFOLiGONo,8
|
|
10
|
+
borgapi-0.7.1.dist-info/RECORD,,
|
borgapi-0.6.1.dist-info/RECORD
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
borgapi/__init__.py,sha256=djERfEQ24Ray3amPnnW4kad1OLKVqe5hLWnBxyTI_vM,111
|
|
2
|
-
borgapi/borgapi.py,sha256=tq7nJInEmQay5Aez_rpECmLzmQSNYH6MiYPB3XK1g8o,40094
|
|
3
|
-
borgapi/options.py,sha256=Iqp80mIb4sJY3COVjiokkxLnCJJqHqf2_iVdgPpyeIE,29327
|
|
4
|
-
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
test/test_00_options.py,sha256=cypNm4WNYsAqtbOlqFudPJujE8KUDUSowo5hmqjaSew,2220
|
|
6
|
-
test/borgapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
test/borgapi/test_01_borgapi.py,sha256=jfW5zD_phBIRsnMrqThWm36oibujkHU6Q9S5gGA3t9M,7530
|
|
8
|
-
test/borgapi/test_02_init.py,sha256=VJIqpSG73YMOccsa-pXBcRBbI6MJLACWFCYoVguXnPE,2471
|
|
9
|
-
test/borgapi/test_03_create.py,sha256=f3Z1iQ_Qr06ztUa_r70VbigypPu_-ZL1w0rEheddw0g,4883
|
|
10
|
-
test/borgapi/test_04_extract.py,sha256=zb9UrdpGC-HhrM1O5_evAkrddNvsLGVoumK9LNsLw8E,1598
|
|
11
|
-
test/borgapi/test_05_rename.py,sha256=wRLx44-u6yIa_Z1MpR_BMTwmBtWGItZOqvO99NeuVIo,999
|
|
12
|
-
test/borgapi/test_06_list.py,sha256=2xW5DmQsVaVffX8RaQo5NjNE2QvgSdkiU9UTIwkF46U,2028
|
|
13
|
-
test/borgapi/test_07_diff.py,sha256=qMie1yZKDGCvUn5-pP-B3RCcc2tsJAYZVHnlluctZpw,2075
|
|
14
|
-
test/borgapi/test_08_delete.py,sha256=S0y6-J4qYlipuuZxOGtJoIAIAupGsQpDKk8AW_aGtRM,2050
|
|
15
|
-
test/borgapi/test_09_prune.py,sha256=lZm_gsETCf4m2JhrWblu-E0HMBHKqkYYzSnwHDPW75w,1578
|
|
16
|
-
test/borgapi/test_10_info.py,sha256=zlUsm00p2E71AKJAjQL7AlZqVW4Ua7GgmFY7LhOwuwQ,1624
|
|
17
|
-
test/borgapi/test_11_mount.py,sha256=2bIWHRMWcjnS5rMOR1erxmfNtOzIHaFlGxI_O412W_c,1555
|
|
18
|
-
test/borgapi/test_12_key.py,sha256=vLyCtt0twjmBpfh9-gJ0vXVNq3qnn2VwcN-h_yAUDM8,2514
|
|
19
|
-
test/borgapi/test_13_export_tar.py,sha256=qd1HXdO8y9Gy16JJ80VFkDj0RaJ9JJXu9df69M81dx4,1132
|
|
20
|
-
test/borgapi/test_14_config.py,sha256=eQqHdzYrBEAuAbVXAkTKacFxbXpb89sFrn7h9_OAdW0,1668
|
|
21
|
-
test/borgapi/test_15_benchmark_crud.py,sha256=rabhvkRe5AM8R4HDrFOE0pC9M8alSRtsJripUDTemL4,759
|
|
22
|
-
test/borgapi/test_16_compact.py,sha256=zGsAxv6E4eHgJPxKOJCQrzSEmchLjIPza_PDEO05xmA,1017
|
|
23
|
-
borgapi-0.6.1.dist-info/LICENSE,sha256=g7PjdvkUMJa85GMb6zUCyW8aX_yk5Ym1x7if5W5ULPI,1050
|
|
24
|
-
borgapi-0.6.1.dist-info/METADATA,sha256=FdIoXb0Wxq27FnRVba5dxrqd7PaMVOYy5ZoyXeYh1NE,9085
|
|
25
|
-
borgapi-0.6.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
26
|
-
borgapi-0.6.1.dist-info/top_level.txt,sha256=23Hbpr8OhmPV1WMBvLmZOwEC-3buY84J7ZckxsN7Id4,13
|
|
27
|
-
borgapi-0.6.1.dist-info/RECORD,,
|
test/__init__.py
DELETED
|
File without changes
|
test/borgapi/__init__.py
DELETED
|
File without changes
|
test/borgapi/test_01_borgapi.py
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
"""Test borgapi module"""
|
|
2
|
-
import logging
|
|
3
|
-
import unittest
|
|
4
|
-
from configparser import ConfigParser
|
|
5
|
-
from os import getenv, makedirs, remove
|
|
6
|
-
from os.path import exists, join
|
|
7
|
-
from shutil import rmtree
|
|
8
|
-
|
|
9
|
-
from dotenv import load_dotenv
|
|
10
|
-
|
|
11
|
-
from borgapi import BorgAPI
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# pylint: disable=too-many-public-methods
|
|
15
|
-
class BorgapiTests(unittest.TestCase):
|
|
16
|
-
"""Test for the borgbackup self.api"""
|
|
17
|
-
|
|
18
|
-
# pylint: disable=invalid-name
|
|
19
|
-
@staticmethod
|
|
20
|
-
def assertFileExists(path, msg=None):
|
|
21
|
-
"""Assert if a path exists or not"""
|
|
22
|
-
if not exists(path):
|
|
23
|
-
raise AssertionError(msg or f"{path} does not exist")
|
|
24
|
-
|
|
25
|
-
@staticmethod
|
|
26
|
-
def assertFileNotExists(path, msg=None):
|
|
27
|
-
"""Assert if a path exists or not"""
|
|
28
|
-
if exists(path):
|
|
29
|
-
raise AssertionError(msg or f"{path} does exist")
|
|
30
|
-
|
|
31
|
-
@staticmethod
|
|
32
|
-
def assertKeyExists(key, dictionary, msg=None):
|
|
33
|
-
"""Assert a key exists in a dictionary"""
|
|
34
|
-
if key not in dictionary:
|
|
35
|
-
raise AssertionError(msg or f"{key} does not exist in dictionary")
|
|
36
|
-
|
|
37
|
-
@staticmethod
|
|
38
|
-
def assertKeyNotExists(key, dictionary, msg=None):
|
|
39
|
-
"""Assert a key does not exist in a dictionary"""
|
|
40
|
-
if key in dictionary:
|
|
41
|
-
raise AssertionError(msg or f"{key} exists in dictionary")
|
|
42
|
-
|
|
43
|
-
@staticmethod
|
|
44
|
-
def assertType(obj, type_, msg=None):
|
|
45
|
-
"""Assert an object is an instance of type"""
|
|
46
|
-
if not isinstance(obj, type_):
|
|
47
|
-
raise AssertionError(msg or f"{obj} is not type {type_}, it is {type(obj)}")
|
|
48
|
-
|
|
49
|
-
@staticmethod
|
|
50
|
-
def assertAnyType(obj, *types, msg=None):
|
|
51
|
-
"""Assert an object is an instance of type"""
|
|
52
|
-
if not any([isinstance(obj, t) for t in types]):
|
|
53
|
-
raise AssertionError(
|
|
54
|
-
msg or f"{obj} is not any of {types}; it is {type(obj)}"
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
@staticmethod
|
|
58
|
-
def assertSubclass(obj, class_, msg=None):
|
|
59
|
-
"""Assert an object is an subclass of class"""
|
|
60
|
-
if not issubclass(obj, class_):
|
|
61
|
-
raise AssertionError(msg or f"{obj} is not a subtype of {class_}")
|
|
62
|
-
|
|
63
|
-
@staticmethod
|
|
64
|
-
def assertNone(obj, msg=None):
|
|
65
|
-
"""Assert an object is None"""
|
|
66
|
-
if obj is not None:
|
|
67
|
-
raise AssertionError(msg or f"Value is not None: {obj}")
|
|
68
|
-
|
|
69
|
-
@staticmethod
|
|
70
|
-
def assertNotNone(obj, msg=None):
|
|
71
|
-
"""Assert an object is None"""
|
|
72
|
-
if obj is None:
|
|
73
|
-
raise AssertionError(msg or "Value is None")
|
|
74
|
-
|
|
75
|
-
@staticmethod
|
|
76
|
-
def _try_pass(error, func, *args, **kwargs):
|
|
77
|
-
try:
|
|
78
|
-
func(*args, **kwargs)
|
|
79
|
-
except error:
|
|
80
|
-
pass
|
|
81
|
-
|
|
82
|
-
@staticmethod
|
|
83
|
-
def _make_clean(directory):
|
|
84
|
-
try:
|
|
85
|
-
makedirs(directory)
|
|
86
|
-
except FileExistsError:
|
|
87
|
-
rmtree(directory)
|
|
88
|
-
makedirs(directory)
|
|
89
|
-
|
|
90
|
-
@staticmethod
|
|
91
|
-
def _read_config(string=None, filename=None):
|
|
92
|
-
"""Convert config string into dictionary"""
|
|
93
|
-
config = ConfigParser()
|
|
94
|
-
if filename:
|
|
95
|
-
with open(filename, "r") as fp:
|
|
96
|
-
config.read_file(fp)
|
|
97
|
-
else:
|
|
98
|
-
config.read_string(string)
|
|
99
|
-
return config
|
|
100
|
-
|
|
101
|
-
@staticmethod
|
|
102
|
-
def _display(header, output, single=True):
|
|
103
|
-
"""display captured output"""
|
|
104
|
-
if getenv("BORGAPI_TEST_OUTPUT_DISPLAY"):
|
|
105
|
-
print(header)
|
|
106
|
-
if single:
|
|
107
|
-
print(output)
|
|
108
|
-
else:
|
|
109
|
-
for name, value in output.items():
|
|
110
|
-
print(f"~~~~~~~~~~ {name} ~~~~~~~~~~")
|
|
111
|
-
print(value)
|
|
112
|
-
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
|
|
113
|
-
print()
|
|
114
|
-
|
|
115
|
-
@classmethod
|
|
116
|
-
def setUpClass(cls):
|
|
117
|
-
"""Initalize environment for borg use"""
|
|
118
|
-
|
|
119
|
-
cls.temp = "test/temp"
|
|
120
|
-
cls.data = join(cls.temp, "data")
|
|
121
|
-
cls.repo = join(cls.temp, "repo")
|
|
122
|
-
cls.logs = join(cls.temp, "logs")
|
|
123
|
-
|
|
124
|
-
cls.archive = f"{cls.repo}::1"
|
|
125
|
-
|
|
126
|
-
cls._try_pass(FileNotFoundError, rmtree, cls.data)
|
|
127
|
-
cls._try_pass(FileNotFoundError, rmtree, cls.repo)
|
|
128
|
-
if not getenv("BORGAPI_TEST_KEEP_LOGS"):
|
|
129
|
-
cls._try_pass(FileNotFoundError, rmtree, cls.logs)
|
|
130
|
-
|
|
131
|
-
cls.file_1 = join(cls.data, "file_1.txt")
|
|
132
|
-
cls.file_1_text = "Hello World"
|
|
133
|
-
cls.file_2 = join(cls.data, "file_2.txt")
|
|
134
|
-
cls.file_2_text = "Goodbye Fools"
|
|
135
|
-
cls.file_3 = join(cls.data, "file_3.txt")
|
|
136
|
-
cls.file_3_text = "New File Added"
|
|
137
|
-
|
|
138
|
-
cls._try_pass(FileExistsError, makedirs, cls.data)
|
|
139
|
-
cls._try_pass(FileExistsError, makedirs, cls.repo)
|
|
140
|
-
cls._try_pass(FileExistsError, makedirs, cls.logs)
|
|
141
|
-
load_dotenv("test/res/test_env")
|
|
142
|
-
|
|
143
|
-
@classmethod
|
|
144
|
-
def tearDownClass(cls):
|
|
145
|
-
"""Remove temp directory"""
|
|
146
|
-
if not getenv("BORGAPI_TEST_KEEP_LOGS") and not getenv(
|
|
147
|
-
"BORGAPI_TEST_KEEP_TEMP"
|
|
148
|
-
):
|
|
149
|
-
cls._try_pass(FileNotFoundError, rmtree, cls.temp)
|
|
150
|
-
|
|
151
|
-
def setUp(self):
|
|
152
|
-
"""Setup files data"""
|
|
153
|
-
self._try_pass(FileExistsError, makedirs, self.data)
|
|
154
|
-
self._try_pass(FileExistsError, makedirs, self.repo)
|
|
155
|
-
self._try_pass(FileExistsError, makedirs, self.logs)
|
|
156
|
-
|
|
157
|
-
with open(self.file_1, "w") as fp:
|
|
158
|
-
fp.write(self.file_1_text)
|
|
159
|
-
with open(self.file_2, "w") as fp:
|
|
160
|
-
fp.write(self.file_2_text)
|
|
161
|
-
|
|
162
|
-
self._try_pass(FileNotFoundError, remove, self.file_3)
|
|
163
|
-
|
|
164
|
-
self.api = BorgAPI()
|
|
165
|
-
self.api.init(self.repo)
|
|
166
|
-
|
|
167
|
-
def tearDown(self):
|
|
168
|
-
"""Resets mess made"""
|
|
169
|
-
if not getenv("BORGAPI_TEST_KEEP_TEMP"):
|
|
170
|
-
self._try_pass(FileNotFoundError, rmtree, self.data)
|
|
171
|
-
self._try_pass(FileNotFoundError, rmtree, self.repo)
|
|
172
|
-
if not getenv("BORGAPI_TEST_KEEP_LOGS"):
|
|
173
|
-
self._try_pass(FileNotFoundError, rmtree, self.logs)
|
|
174
|
-
|
|
175
|
-
def _create_default(self):
|
|
176
|
-
self.api.create(self.archive, self.data)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
class SingleTests(BorgapiTests):
|
|
180
|
-
"""Simple Command and Class Methodss tests"""
|
|
181
|
-
|
|
182
|
-
def test_01_borgapi_logger(self):
|
|
183
|
-
"""Verify loggers are setup correctly for borgapi"""
|
|
184
|
-
loggers = logging.root.manager.loggerDict
|
|
185
|
-
self.assertIn("borgapi", loggers, "borgapi logger not present")
|
|
186
|
-
self.assertIn("borg", loggers, "borg logger not present")
|
|
187
|
-
|
|
188
|
-
def test_02_set_environ(self):
|
|
189
|
-
"""Set new env variable"""
|
|
190
|
-
key = "TEST_VARIABLE"
|
|
191
|
-
|
|
192
|
-
with open(self.file_3, "w") as fp:
|
|
193
|
-
fp.write(f"{key}={self.file_3_text}")
|
|
194
|
-
self.api.set_environ(filename=self.file_3)
|
|
195
|
-
got = getenv(key)
|
|
196
|
-
self.assertEqual(got, self.file_3_text)
|
|
197
|
-
|
|
198
|
-
self.api.set_environ(dictionary={key: self.file_1_text})
|
|
199
|
-
got = getenv(key)
|
|
200
|
-
self.assertEqual(got, self.file_1_text)
|
|
201
|
-
|
|
202
|
-
self.api.set_environ(**{key: self.file_2_text})
|
|
203
|
-
got = getenv(key)
|
|
204
|
-
self.assertEqual(got, self.file_2_text)
|
|
205
|
-
|
|
206
|
-
def test_03_unset_environ(self):
|
|
207
|
-
"""Remove env variable"""
|
|
208
|
-
key = "TEST_VARIABLE"
|
|
209
|
-
|
|
210
|
-
self.api.set_environ(**{key: self.file_1_text})
|
|
211
|
-
got = getenv(key)
|
|
212
|
-
self.assertEqual(got, self.file_1_text)
|
|
213
|
-
self.api.unset_environ(key)
|
|
214
|
-
got = getenv(key)
|
|
215
|
-
self.assertFalse(got)
|
|
216
|
-
|
|
217
|
-
with open(self.file_3, "w") as fp:
|
|
218
|
-
fp.write(f"{key}={self.file_3_text}")
|
|
219
|
-
self.api.set_environ(filename=self.file_3)
|
|
220
|
-
self.api.unset_environ()
|
|
221
|
-
got = getenv(key)
|
|
222
|
-
self.assertFalse(got)
|
|
223
|
-
|
|
224
|
-
@unittest.skip("WIP: Don't know what locking would be used for")
|
|
225
|
-
def test_04_lock(self):
|
|
226
|
-
"""Don't know what locking would be used for, so don't know how to test"""
|
test/borgapi/test_02_init.py
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"""Test init command"""
|
|
2
|
-
from os import urandom
|
|
3
|
-
from os.path import join
|
|
4
|
-
|
|
5
|
-
from borg.repository import Repository
|
|
6
|
-
|
|
7
|
-
from .test_01_borgapi import BorgapiTests
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class InitTests(BorgapiTests):
|
|
11
|
-
"""Init command tests"""
|
|
12
|
-
|
|
13
|
-
def setUp(self):
|
|
14
|
-
super().setUp()
|
|
15
|
-
self._make_clean(self.repo)
|
|
16
|
-
|
|
17
|
-
def test_01_basic(self):
|
|
18
|
-
"""Initalize new repository"""
|
|
19
|
-
self.api.init(self.repo)
|
|
20
|
-
self.assertFileExists(join(self.repo, "README"))
|
|
21
|
-
|
|
22
|
-
def test_02_already_exists(self):
|
|
23
|
-
"""Initalize a repo in a directory where one already exists"""
|
|
24
|
-
self.api.init(self.repo)
|
|
25
|
-
self.assertRaises(
|
|
26
|
-
Repository.AlreadyExists,
|
|
27
|
-
self.api.init,
|
|
28
|
-
self.repo,
|
|
29
|
-
msg="Duplicate repositroy overwrites old repo",
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
def test_03_path_exists(self):
|
|
33
|
-
"""Initalize a repo in a directory where other data exists"""
|
|
34
|
-
self.api.init(self.repo)
|
|
35
|
-
self.assertRaises(
|
|
36
|
-
Repository.PathAlreadyExists,
|
|
37
|
-
self.api.init,
|
|
38
|
-
self.data,
|
|
39
|
-
msg="Repositroy overwrites directory with other data",
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
def test_04_make_parent(self):
|
|
43
|
-
"""Init a repo where parents don't exist with different flags"""
|
|
44
|
-
deep_repo = join(self.repo, "make/parents")
|
|
45
|
-
|
|
46
|
-
self.assertRaises(
|
|
47
|
-
Repository.ParentPathDoesNotExist,
|
|
48
|
-
self.api.init,
|
|
49
|
-
deep_repo,
|
|
50
|
-
msg="Repository made with missing directories",
|
|
51
|
-
)
|
|
52
|
-
self.api.init(deep_repo, make_parent_dirs=True)
|
|
53
|
-
output = self.api.list(deep_repo, json=True)
|
|
54
|
-
self.assertKeyExists("repository", output, "Repository not initalzied")
|
|
55
|
-
|
|
56
|
-
def test_05_storage_quota(self):
|
|
57
|
-
"""Limit the size of the repo"""
|
|
58
|
-
self.api.init(self.repo, storage_quota="10M")
|
|
59
|
-
with open(self.file_3, "wb") as fp:
|
|
60
|
-
fp.write(urandom(10 * 1024 * 1024))
|
|
61
|
-
self.assertRaises(
|
|
62
|
-
Repository.StorageQuotaExceeded,
|
|
63
|
-
self.api.create,
|
|
64
|
-
self.archive,
|
|
65
|
-
self.data,
|
|
66
|
-
msg="Stored more than quota allowed",
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
def test_06_append_only(self):
|
|
70
|
-
"""Repo in append only mode"""
|
|
71
|
-
self.api.init(self.repo, append_only=True)
|
|
72
|
-
output = self.api.config(self.repo, list=True)
|
|
73
|
-
config = self._read_config(output)
|
|
74
|
-
self.assertEqual(
|
|
75
|
-
config["repository"]["append_only"],
|
|
76
|
-
"1",
|
|
77
|
-
"Repo not in append_only mode",
|
|
78
|
-
)
|