1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
# FIXME: port properly!
"""Shared functionality.
Part of the tagit module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2022
"""
# standard imports
from collections import namedtuple
import logging
import os
import pkgutil
import platform
import re
import typing
import warnings
# exports
__all__: typing.Sequence[str] = (
'Resolution',
'Struct',
'clamp',
'fileopen',
'flatten',
'fst',
'import_all',
'is_hex',
'is_list',
'magnitude_fmt',
'truncate_dir',
)
## code ##
logger = logging.getLogger(__name__)
fst = lambda lst: lst[0]
def is_list(cand):
"""Return true if *cand* is a list, a set, or a tuple"""
return isinstance(cand, list) or isinstance(cand, set) or isinstance(cand, tuple)
def import_all(module, exclude=None, verbose=False):
"""Recursively import all submodules of *module*.
*exclude* is a set of submodule names which will
be omitted. With *verbose*, all imports are logged
with level info. Returns all imported modules.
>>> import tagit
>>> import_all(tagit, exclude={'tagit.shared.external'})
"""
exclude = set([] if exclude is None else exclude)
imports = []
for importer, name, ispkg in pkgutil.iter_modules(module.__path__, module.__name__ + '.'):
if ispkg and all(re.match(exl, name) is None for exl in exclude):
if verbose:
logger.info(f'importing: {name}')
try:
module = __import__(name, fromlist='dummy')
imports.append(module)
imports += import_all(module, exclude, verbose)
except Exception as e:
logger.error(f'importing: {name}')
return imports
def clamp(value, hi, lo=0):
"""Restrain a *value* to the range *lo* to *hi*."""
return max(lo, min(hi, value))
Resolution = namedtuple('resolution', ('width', 'height'))
def truncate_dir(path, cutoff=3):
"""Remove path up to last *cutoff* directories"""
if cutoff < 0: raise ValueError('path cutoff must be positive')
dirs = os.path.dirname(path).split(os.path.sep)
last_dirs = dirs[max(0, len(dirs) - cutoff):]
prefix = ''
if os.path.isabs(path) and len(last_dirs) == len(dirs):
prefix = os.path.sep
return prefix + os.path.join(*(last_dirs + [os.path.basename(path)]))
def magnitude_fmt(num, suffix='iB', scale=1024):
"""Human-readable number format.
adapted from Sridhar Ratnakumar, 2009
https://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size
"""
for unit in ['','K','M','G','T','P','E','Z']:
if abs(num) < scale:
return "%3.1f%s%s" % (num, unit, suffix)
num /= scale
return "%.1f%s%s" % (num, 'Y', suffix)
class Struct(dict):
"""Dict with item access as members.
>>> tup = Struct(timestamp=123, series=['1','2','3'])
>>> tup.timestamp
123
>>> tup['timestamp']
123
"""
def __getattr__(self, name):
return self[name]
def __setattr__(self, name, value):
self[name] = value
def flatten(lst):
flat = []
for itm in lst:
flat.extend(list(itm))
return flat
def is_hex(string):
"""Return True if the *string* can be interpreted as a hex value."""
try:
int(string, 16)
return True
except ValueError:
return False
except TypeError:
return False
def fileopen(pth):
"""Open a file in the preferred application as a subprocess.
This operation is platform dependent.
"""
try:
binopen = {
"Linux" : "xdg-open", # Linux
"darwin" : "open", # MAX OS X
"Windows" : "start", # Windows
}.get(platform.system())
subprocess.call((binopen, pth))
except KeyError:
warnings.warn('Unknown platform {}'.format(platform.system()))
## EOF ##
|