python3-cyberfusion-file-support 1.1.1.2__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.
@@ -0,0 +1,160 @@
1
+ """Classes for files."""
2
+
3
+ import difflib
4
+ import filecmp
5
+ import os
6
+ from typing import List, Optional, Union
7
+
8
+ from cyberfusion.Common import get_tmp_file
9
+ from cyberfusion.QueueSupport import Queue
10
+ from cyberfusion.QueueSupport.items.command import CommandItem
11
+ from cyberfusion.QueueSupport.items.copy import CopyItem
12
+ from cyberfusion.QueueSupport.items.unlink import UnlinkItem
13
+
14
+
15
+ class _DestinationFile:
16
+ """Represents destination file."""
17
+
18
+ def __init__(self, *, path: str) -> None:
19
+ """Set attributes."""
20
+ self.path = path
21
+
22
+ @property
23
+ def exists(self) -> bool:
24
+ """Get if exists."""
25
+ return os.path.exists(self.path)
26
+
27
+ @property
28
+ def contents(self) -> Optional[str]:
29
+ """Get contents."""
30
+ if self.exists:
31
+ with open(self.path, "r") as f:
32
+ return f.read()
33
+
34
+ return None
35
+
36
+
37
+ class DestinationFileReplacement:
38
+ """Represents file that will replace destination file."""
39
+
40
+ def __init__(
41
+ self,
42
+ queue: Queue,
43
+ *,
44
+ contents: Union[str, bytes],
45
+ destination_file_path: str,
46
+ default_comment_character: Optional[str] = None,
47
+ command: Optional[List[str]] = None,
48
+ reference: Optional[str] = None,
49
+ ) -> None:
50
+ """Set attributes.
51
+
52
+ 'default_comment_character' has no effect when 'contents' is not string.
53
+ """
54
+ self.queue = queue
55
+ self._contents = contents
56
+ self.default_comment_character = default_comment_character
57
+ self.command = command
58
+ self.reference = reference
59
+
60
+ self.tmp_path = get_tmp_file()
61
+ self.destination_file = _DestinationFile(path=destination_file_path)
62
+
63
+ self._write_to_tmp_file()
64
+
65
+ @property
66
+ def contents(self) -> Union[str, bytes]:
67
+ """Get contents."""
68
+ if not isinstance(self._contents, str):
69
+ return self._contents
70
+
71
+ if self._contents != "" and not self._contents.endswith(
72
+ "\n"
73
+ ): # Some programs require newline to consider last line completed
74
+ raise ValueError
75
+
76
+ if not self.default_comment_character:
77
+ return self._contents
78
+
79
+ default_comment = f"{self.default_comment_character} Update this file via your management interface.\n"
80
+ default_comment += (
81
+ f"{self.default_comment_character} Your changes will be overwritten.\n"
82
+ )
83
+ default_comment += "\n"
84
+
85
+ return default_comment + self._contents
86
+
87
+ def _write_to_tmp_file(self) -> None:
88
+ """Write contents to tmp file."""
89
+ if isinstance(self.contents, bytes):
90
+ open_mode = "wb"
91
+ else:
92
+ open_mode = "w"
93
+
94
+ with open(self.tmp_path, open_mode) as f:
95
+ f.write(self.contents)
96
+
97
+ @property
98
+ def changed(self) -> bool:
99
+ """Get if destination file will change."""
100
+ return not self.destination_file.exists or not filecmp.cmp(
101
+ self.tmp_path, self.destination_file.path
102
+ )
103
+
104
+ @property
105
+ def differences(self) -> List[str]:
106
+ """Get differences with destination file.
107
+
108
+ No differences are returned when contents is not string.
109
+ """
110
+ if not isinstance(self._contents, str):
111
+ return []
112
+
113
+ results = []
114
+
115
+ for line in difflib.unified_diff(
116
+ (
117
+ self.destination_file.contents.splitlines()
118
+ if self.destination_file.contents
119
+ else []
120
+ ),
121
+ self.contents.splitlines(), # type: ignore[arg-type]
122
+ fromfile=self.tmp_path,
123
+ tofile=self.destination_file.path,
124
+ lineterm="",
125
+ n=0,
126
+ ):
127
+ results.append(line)
128
+
129
+ return results
130
+
131
+ def add_to_queue(self) -> None:
132
+ """Add items for replacement to queue."""
133
+ if self.changed:
134
+ # Copy when changed and always unlink, instead of move when changed
135
+ # and unlink when changed. MoveItem copies metadata (which means
136
+ # mode etc. of destination file is incorrect, as set to the tmp file
137
+ # until corrected by later queue items). CopyItem does not copy
138
+ # metadata, so if the destination file already exists, its mode
139
+ # etc. is unchanged.
140
+
141
+ self.queue.add(
142
+ CopyItem(
143
+ source=self.tmp_path,
144
+ destination=self.destination_file.path,
145
+ reference=self.reference,
146
+ ),
147
+ )
148
+
149
+ if self.command:
150
+ self.queue.add(
151
+ CommandItem(command=self.command, reference=self.reference),
152
+ )
153
+
154
+ self.queue.add(
155
+ UnlinkItem(
156
+ path=self.tmp_path,
157
+ hide_outcomes=True,
158
+ reference=self.reference,
159
+ ),
160
+ )
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.1
2
+ Name: python3-cyberfusion-file-support
3
+ Version: 1.1.1.2
4
+ Summary: Library for idempotent writing to files.
5
+ Author-email: Cyberfusion <support@cyberfusion.io>
6
+ Project-URL: Source, https://github.com/CyberfusionIO/python3-cyberfusion-file-support
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: python3-cyberfusion-common ~=2.0
9
+ Requires-Dist: python3-cyberfusion-queue-support ~=1.1
10
+
11
+ # python3-cyberfusion-file-support
12
+
13
+ Library for idempotent writing to files.
14
+
15
+ # Install
16
+
17
+ ## PyPI
18
+
19
+ Run the following command to install the package from PyPI:
20
+
21
+ pip3 install python3-cyberfusion-file-support
22
+
23
+ ## Debian
24
+
25
+ Run the following commands to build a Debian package:
26
+
27
+ mk-build-deps -i -t 'apt -o Debug::pkgProblemResolver=yes --no-install-recommends -y'
28
+ dpkg-buildpackage -us -uc
29
+
30
+ # Configure
31
+
32
+ No configuration is supported.
33
+
34
+ # Usage
35
+
36
+ ## Example
37
+
38
+ ```python
39
+ from cyberfusion.QueueSupport import Queue
40
+ from cyberfusion.FileSupport import DestinationFileReplacement
41
+
42
+ queue = Queue()
43
+
44
+ tmp_file = DestinationFileReplacement(
45
+ queue,
46
+ contents="foobar",
47
+ destination_file_path="/tmp/foobar.txt",
48
+ default_comment_character=None,
49
+ command=["true"],
50
+ )
51
+
52
+ print(tmp_file.differences)
53
+
54
+ tmp_file.add_copy_to_queue()
55
+
56
+ queue.process(preview=...)
57
+ ```
@@ -0,0 +1,5 @@
1
+ cyberfusion/FileSupport/__init__.py,sha256=dKoNyd7xBPOBknO7HJQYYgC5tgJo_AFHqgC8B43a0Eo,4858
2
+ python3_cyberfusion_file_support-1.1.1.2.dist-info/METADATA,sha256=lPn_1S3FUpldtzkWUXZInq-2FA3Gf-VV5aui9wNfZw0,1276
3
+ python3_cyberfusion_file_support-1.1.1.2.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
4
+ python3_cyberfusion_file_support-1.1.1.2.dist-info/top_level.txt,sha256=ss011q9S6SL_KIIyq7iujFmIYa0grSjlnInO7cDkeag,12
5
+ python3_cyberfusion_file_support-1.1.1.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (73.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+