skilleter-modules 0.0.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.
@@ -0,0 +1,154 @@
1
+ #! /usr/bin/env python3
2
+
3
+ ################################################################################
4
+ """ Thingy file handling functions
5
+
6
+ Copyright (C) 2017-18 John Skilleter
7
+
8
+ High-level file access functions not provided by the Python libraries
9
+ """
10
+ ################################################################################
11
+
12
+ import os
13
+ import shutil
14
+ import subprocess
15
+
16
+ ################################################################################
17
+
18
+ def is_binary_file(filename):
19
+ """ Return True if there is a strong likelihood that the specified file
20
+ is binary. """
21
+
22
+ return file_type(filename, mime=True).endswith('binary')
23
+
24
+ ################################################################################
25
+
26
+ def file_type(filename, mime=False):
27
+ """ Return a textual description of the file type """
28
+
29
+ # The file utility does not return an error if the file does not exist or
30
+ # is not a file, so we have to do that.
31
+
32
+ if not os.path.isfile(filename) or not os.access(filename, os.R_OK):
33
+ raise FileNotFoundError('Unable to access %s' % filename)
34
+
35
+ cmd = ['file', '--brief']
36
+
37
+ if mime:
38
+ cmd.append('--mime')
39
+
40
+ cmd.append(filename)
41
+
42
+ result = subprocess.run(cmd, capture_output=True, check=False, text=True)
43
+
44
+ return result.stdout.split('\n')[0] if result.returncode==0 else None
45
+
46
+ ################################################################################
47
+
48
+ def format_size(size, always_suffix=False):
49
+ """ Convert a memory/disk size into appropriately-scaled units in bytes,
50
+ MiB, GiB, TiB as a string """
51
+
52
+ # Keep all the maths positive
53
+
54
+ if size < 0:
55
+ size = -size
56
+ sign = '-'
57
+ else:
58
+ sign = ''
59
+
60
+ # Default divisor and number of decimal places output
61
+
62
+ div = 1
63
+
64
+ # Step through the multipliers
65
+
66
+ for units in (' bytes' if always_suffix else '', ' KiB', ' MiB', ' GiB', ' TiB'):
67
+ # If we can't scale up to this multiplier quit the loop
68
+
69
+ if size // div < 1024:
70
+ break
71
+
72
+ # Increase the divisor and set the number of decimal places
73
+ # to 1 (set to 0 when we don't have a divisor).
74
+
75
+ div *= 1024
76
+ else:
77
+ units = ' PiB'
78
+
79
+ # Calculate the size in 10ths so the we get the first digit after
80
+ # the decimal point, doing all the work in the integer domain to
81
+ # avoid rounding errors.
82
+
83
+ size_x_10 = (size * 10) // div
84
+
85
+ # If the decimal part would be '.0' don't output it
86
+
87
+ if size_x_10 % 10 == 0:
88
+ return '%s%d%s' % (sign, size_x_10 // 10, units)
89
+
90
+ return '%s%d.%d%s' % (sign, size_x_10 // 10, size_x_10 % 10, units)
91
+
92
+ ################################################################################
93
+
94
+ def backup(filename, extension='bak', copyfile=True, timestamps=True):
95
+ """ Create a backup of a file by copying or renaming it into a file with extension
96
+ .bak, deleting any existing file with that name.
97
+
98
+ Copies the file by default, unless copyfile is False
99
+ Sets the timetamps of the backup file to be the same as the original, unless
100
+ timestamps is False."""
101
+
102
+ # Do nothing if the file does not exist
103
+
104
+ if not os.path.isfile(filename):
105
+ return
106
+
107
+ # Split on the dot characters
108
+
109
+ filename_comp = filename.split('.')
110
+
111
+ # Replace the extension with the specified on (or 'bak' by
112
+ # default) or add it if the filename did not have an extension.
113
+
114
+ if len(filename_comp) > 1:
115
+ filename_comp[-1] = extension
116
+ else:
117
+ filename_comp.append(extension)
118
+
119
+ backupname = '.'.join(filename_comp)
120
+
121
+ # Remove any existing backup file
122
+
123
+ if os.path.isfile(backupname):
124
+ os.unlink(backupname)
125
+
126
+ # Create the backup by copying or renaming the file, optionally preserving the
127
+ # timestamp of the original file
128
+
129
+ if timestamps:
130
+ file_info = os.stat(filename)
131
+
132
+ if copyfile:
133
+ shutil.copyfile(filename, backupname)
134
+ else:
135
+ os.rename(filename, backupname)
136
+
137
+ if timestamps:
138
+ os.utime(backupname, ns=(file_info.st_atime_ns, file_info.st_mtime_ns))
139
+
140
+ ################################################################################
141
+ # Test code
142
+
143
+ if __name__ == "__main__":
144
+ print('Is /bin/sh binary: %s' % is_binary_file('/bin/sh'))
145
+ print('Is files.py binary: %s' % is_binary_file('files.py'))
146
+ print('')
147
+
148
+ for mimeflag in (False, True):
149
+ print('/bin/sh is: %s' % file_type('/bin/sh', mimeflag))
150
+ print('/bin/dash is: %s' % file_type('/bin/dash', mimeflag))
151
+ print('')
152
+
153
+ for sizevalue in (0, 1, 999, 1024, 1025, 1.3 * 1024, 2**32 - 1, 2**64 + 2**49):
154
+ print('%24d is %s' % (sizevalue, format_size(sizevalue)))