cs-py-func 20220619__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.
cs/py/func.py ADDED
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/python
2
+ #
3
+ # Convenience routines for python functions.
4
+ # - Cameron Simpson <cs@cskk.id.au> 15apr2014
5
+ #
6
+
7
+ r'''
8
+ Convenience facilities related to Python functions.
9
+ '''
10
+
11
+ from functools import partial
12
+ from pprint import pformat
13
+ from cs.deco import decorator
14
+ from cs.py.stack import caller
15
+ from cs.py3 import unicode, raise_from
16
+ from cs.x import X
17
+
18
+ __version__ = '20220619'
19
+
20
+ DISTINFO = {
21
+ 'keywords': ["python2", "python3"],
22
+ 'classifiers': [
23
+ "Programming Language :: Python",
24
+ "Programming Language :: Python :: 2",
25
+ "Programming Language :: Python :: 3",
26
+ ],
27
+ 'install_requires': [
28
+ 'cs.deco',
29
+ 'cs.py.stack',
30
+ 'cs.py3',
31
+ 'cs.x',
32
+ ],
33
+ }
34
+
35
+ def funcname(func):
36
+ ''' Return a name for the supplied function `func`.
37
+ Several objects do not have a __name__ attribute, such as partials.
38
+ '''
39
+ try:
40
+ return func.__qualname__
41
+ except AttributeError:
42
+ try:
43
+ return func.__name__
44
+ except AttributeError:
45
+ if isinstance(func, partial):
46
+ return "partial(%s)" % (funcname(func.func),)
47
+ return str(func)
48
+
49
+ def funccite(func):
50
+ ''' Return a citation for a function (name and code location).
51
+ '''
52
+ try:
53
+ code = func.__code__
54
+ except AttributeError:
55
+ return "%s[no.__code__]" % (repr(func),)
56
+ return "%s[%s:%d]" % (funcname(func), code.co_filename, code.co_firstlineno)
57
+
58
+ def func_a_kw_fmt(func, *a, **kw):
59
+ ''' Prepare a percent-format string and associated argument list
60
+ describing a call to `func(*a,**kw)`.
61
+ Return `format,args`.
62
+
63
+ The `func` argument can also be a string,
64
+ presumably a prepared description of `func` such as `funccite(func)`.
65
+ '''
66
+ av = [
67
+ func if isinstance(func, str) else getattr(func, '__name__', str(func))
68
+ ]
69
+ afv = ['%r'] * len(a)
70
+ av.extend(a)
71
+ afv.extend(['%s=%r'] * len(kw))
72
+ for kv in kw.items():
73
+ av.extend(kv)
74
+ return '%s(' + ','.join(afv) + ')', av
75
+
76
+ def callif(doit, func, *a, **kw):
77
+ ''' Call `func(*a,**kw)` if `doit` is true
78
+ otherwise just print it out.
79
+
80
+ The parameter `func` may be preceeded optionally by a `dict`
81
+ containing modes. The current modes are:
82
+ * `'print'`: the print function, default the builtin `print`
83
+ '''
84
+ if isinstance(func, dict):
85
+ modes = func
86
+ a = list(a)
87
+ func = a.pop(0)
88
+ else:
89
+ modes = {}
90
+ modes.setdefault('print', print)
91
+ if doit:
92
+ return func(*a, **kw)
93
+ fmt, av = func_a_kw_fmt(func, *a, **kw)
94
+ modes['print'](fmt % tuple(av))
95
+ return None
96
+
97
+ @decorator
98
+ # pylint: disable=too-many-arguments
99
+ def trace(
100
+ func,
101
+ call=True,
102
+ retval=False,
103
+ exception=False,
104
+ use_pformat=False,
105
+ with_caller=False,
106
+ with_pfx=False,
107
+ ):
108
+ ''' Decorator to report the call and return of a function.
109
+ '''
110
+
111
+ citation = funccite(func)
112
+
113
+ def traced_function_wrapper(*a, **kw):
114
+ ''' Wrapper for `func` to trace call and return.
115
+ '''
116
+ # late import so that we can use this in modules we import
117
+ # pylint: disable=import-outside-toplevel
118
+ if with_pfx:
119
+ try:
120
+ from cs.pfx import XP as xlog
121
+ except ImportError:
122
+ xlog = X
123
+ else:
124
+ xlog = X
125
+ log_cite = citation
126
+ if with_caller:
127
+ log_cite = log_cite + "from[%s]" % (caller(),)
128
+ if call:
129
+ fmt, av = func_a_kw_fmt(log_cite, *a, **kw)
130
+ xlog("CALL " + fmt, *av)
131
+ try:
132
+ result = func(*a, **kw)
133
+ except Exception as e:
134
+ if exception:
135
+ xlog("CALL %s RAISE %r", log_cite, e)
136
+ raise
137
+ else:
138
+ if retval:
139
+ xlog(
140
+ "CALL %s RETURN %s", log_cite,
141
+ (pformat if use_pformat else repr)(result)
142
+ )
143
+ return result
144
+
145
+ traced_function_wrapper.__name__ = "@trace(%s)" % (citation,)
146
+ traced_function_wrapper.__doc__ = "@trace(%s)\n\n" + (func.__doc__ or '')
147
+ return traced_function_wrapper
148
+
149
+ def callmethod_if(o, method, default=None, a=None, kw=None):
150
+ ''' Call the named `method` on the object `o` if it exists.
151
+
152
+ If it does not exist, return `default` (which defaults to None).
153
+ Otherwise call getattr(o, method)(*a, **kw).
154
+ `a` defaults to ().
155
+ `kw` defaults to {}.
156
+ '''
157
+ try:
158
+ m = getattr(o, method)
159
+ except AttributeError:
160
+ return default
161
+ if a is None:
162
+ a = ()
163
+ if kw is None:
164
+ kw = {}
165
+ return m(*a, **kw)
166
+
167
+ def prop(func):
168
+ ''' A substitute for the builtin @property.
169
+
170
+ The builtin @property decorator lets internal AttributeErrors escape.
171
+ While that can support properties that appear to exist conditionally,
172
+ in practice this is almost never what I want, and it masks deeper errors.
173
+ Hence this wrapper for @property that transmutes internal AttributeErrors
174
+ into RuntimeErrors.
175
+ '''
176
+
177
+ # pylint: disable=inconsistent-return-statements
178
+ def prop_wrapper(*a, **kw):
179
+ try:
180
+ return func(*a, **kw)
181
+ except AttributeError as e:
182
+ raise_from(RuntimeError("inner function %s raised %s" % (func, e)), e)
183
+
184
+ prop_wrapper.__name__ = "@prop(%s)" % (funcname(func),)
185
+ return property(prop_wrapper)
186
+
187
+ def derived_property(
188
+ func,
189
+ original_revision_name='_revision',
190
+ lock_name='_lock',
191
+ property_name=None,
192
+ unset_object=None
193
+ ):
194
+ ''' A property which must be recomputed
195
+ if the reference revision (attached to self)
196
+ exceeds the snapshot revision.
197
+ '''
198
+ if property_name is None:
199
+ property_name = '_' + func.__name__
200
+ # the property used to track the reference revision
201
+ property_revision_name = property_name + '__revision'
202
+
203
+ def property_value(self):
204
+ ''' Attempt lockless fetch of property first.
205
+ Use lock if property is unset and up to date.
206
+ '''
207
+ # poll outside lock
208
+ try:
209
+ p = getattr(self, property_name, unset_object)
210
+ p_revision = getattr(self, property_revision_name, 0)
211
+ o_revision = getattr(self, original_revision_name)
212
+ if p is unset_object or p_revision < o_revision:
213
+ with getattr(self, lock_name):
214
+ # repoll value inside lock
215
+ p = getattr(self, property_name, unset_object)
216
+ p_revision = getattr(self, property_revision_name, 0)
217
+ o_revision = getattr(self, original_revision_name)
218
+ if p is unset_object or p_revision < o_revision:
219
+ p = func(self)
220
+ setattr(self, property_name, p)
221
+ setattr(self, property_revision_name, o_revision)
222
+ except AttributeError as e:
223
+ raise_from(RuntimeError("AttributeError: %s" % (e,)), e)
224
+ return p
225
+
226
+ return property(property_value)
227
+
228
+ def derived_from(property_name):
229
+ ''' A property which must be recomputed
230
+ if the revision of another property exceeds the snapshot revision.
231
+ '''
232
+ return partial(
233
+ derived_property,
234
+ original_revision_name='_' + property_name + '__revision'
235
+ )
236
+
237
+ def yields_type(func, basetype):
238
+ ''' Decorator which checks that a generator yields values of type `basetype`.
239
+ '''
240
+ citation = funccite(func)
241
+
242
+ def check_yields_type(*a, **kw):
243
+ for item in func(*a, **kw):
244
+ if not isinstance(item, basetype):
245
+ raise TypeError(
246
+ "wrong type yielded from func %s: expected subclass of %s, got %s: %r"
247
+ % (citation, basetype, type(item), item)
248
+ )
249
+ yield item
250
+
251
+ check_yields_type.__name__ = (
252
+ 'check_yields_type[%s,basetype=%s]' % (citation, basetype)
253
+ )
254
+ return check_yields_type
255
+
256
+ def returns_type(func, basetype):
257
+ ''' Decrator which checks that a function returns values of type `basetype`.
258
+ '''
259
+ citation = funccite(func)
260
+
261
+ def check_returns_type(*a, **kw):
262
+ retval = func(*a, **kw)
263
+ if not isinstance(retval, basetype):
264
+ raise TypeError(
265
+ "wrong type returned from func %s: expected subclass of %s, got %s: %r"
266
+ % (citation, basetype, type(retval), retval)
267
+ )
268
+ return retval
269
+
270
+ check_returns_type.__name__ = (
271
+ 'check_returns_type[%s,basetype=%s]' % (citation, basetype)
272
+ )
273
+ return check_returns_type
274
+
275
+ def yields_str(func):
276
+ ''' Decorator for generators which should yield strings.
277
+ '''
278
+ return yields_type(func, (str, unicode))
279
+
280
+ def returns_bool(func):
281
+ ''' Decorator for functions which should return Booleans.
282
+ '''
283
+ return returns_type(func, bool)
284
+
285
+ def returns_str(func):
286
+ ''' Decorator for functions which should return strings.
287
+ '''
288
+ return returns_type(func, (str, unicode))
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.1
2
+ Name: cs.py.func
3
+ Version: 20220619
4
+ Summary: Convenience facilities related to Python functions.
5
+ Home-page: https://bitbucket.org/cameron_simpson/css/commits/all
6
+ Author: Cameron Simpson
7
+ Author-email: Cameron Simpson <cs@cskk.id.au>
8
+ License: GNU General Public License v3 or later (GPLv3+)
9
+ Project-URL: URL, https://bitbucket.org/cameron_simpson/css/commits/all
10
+ Keywords: python2,python3
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 2
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: cs.deco (>=20220327)
21
+ Requires-Dist: cs.py.stack (>=20220429)
22
+ Requires-Dist: cs.py3 (>=20220523)
23
+ Requires-Dist: cs.x (>=20211208)
24
+
25
+ Convenience facilities related to Python functions.
26
+
27
+ *Latest release 20220619*:
28
+ @trace: rename some parameters, add with_caller.
29
+
30
+ ## Function `callif(doit, func, *a, **kw)`
31
+
32
+ Call `func(*a,**kw)` if `doit` is true
33
+ otherwise just print it out.
34
+
35
+ The parameter `func` may be preceeded optionally by a `dict`
36
+ containing modes. The current modes are:
37
+ * `'print'`: the print function, default the builtin `print`
38
+
39
+ ## Function `callmethod_if(o, method, default=None, a=None, kw=None)`
40
+
41
+ Call the named `method` on the object `o` if it exists.
42
+
43
+ If it does not exist, return `default` (which defaults to None).
44
+ Otherwise call getattr(o, method)(*a, **kw).
45
+ `a` defaults to ().
46
+ `kw` defaults to {}.
47
+
48
+ ## Function `derived_from(property_name)`
49
+
50
+ A property which must be recomputed
51
+ if the revision of another property exceeds the snapshot revision.
52
+
53
+ ## Function `derived_property(func, original_revision_name='_revision', lock_name='_lock', property_name=None, unset_object=None)`
54
+
55
+ A property which must be recomputed
56
+ if the reference revision (attached to self)
57
+ exceeds the snapshot revision.
58
+
59
+ ## Function `func_a_kw_fmt(func, *a, **kw)`
60
+
61
+ Prepare a percent-format string and associated argument list
62
+ describing a call to `func(*a,**kw)`.
63
+ Return `format,args`.
64
+
65
+ The `func` argument can also be a string,
66
+ presumably a prepared description of `func` such as `funccite(func)`.
67
+
68
+ ## Function `funccite(func)`
69
+
70
+ Return a citation for a function (name and code location).
71
+
72
+ ## Function `funcname(func)`
73
+
74
+ Return a name for the supplied function `func`.
75
+ Several objects do not have a __name__ attribute, such as partials.
76
+
77
+ ## Function `prop(func)`
78
+
79
+ A substitute for the builtin @property.
80
+
81
+ The builtin @property decorator lets internal AttributeErrors escape.
82
+ While that can support properties that appear to exist conditionally,
83
+ in practice this is almost never what I want, and it masks deeper errors.
84
+ Hence this wrapper for @property that transmutes internal AttributeErrors
85
+ into RuntimeErrors.
86
+
87
+ ## Function `returns_bool(func)`
88
+
89
+ Decorator for functions which should return Booleans.
90
+
91
+ ## Function `returns_str(func)`
92
+
93
+ Decorator for functions which should return strings.
94
+
95
+ ## Function `returns_type(func, basetype)`
96
+
97
+ Decrator which checks that a function returns values of type `basetype`.
98
+
99
+ ## Function `trace(*da, **dkw)`
100
+
101
+ Decorator to report the call and return of a function.
102
+
103
+ ## Function `yields_str(func)`
104
+
105
+ Decorator for generators which should yield strings.
106
+
107
+ ## Function `yields_type(func, basetype)`
108
+
109
+ Decorator which checks that a generator yields values of type `basetype`.
110
+
111
+ # Release Log
112
+
113
+
114
+
115
+ *Release 20220619*:
116
+ @trace: rename some parameters, add with_caller.
117
+
118
+ *Release 20220311.1*:
119
+ New callif(doit[,modes],func,*a,**kw) to call or print a function, used in -n/--dry-run stuff sometimes.
120
+
121
+ *Release 20220311*:
122
+ * @trace: new pprint=False option to use pprint.pformat instead of repr for the return value.
123
+ * @trace: bugfix use of retval parameter.
124
+
125
+ *Release 20210913*:
126
+ New func_a_kw_fmt(func,*a,**kw) imported from cs.pfx, hooked it into @trace.
127
+
128
+ *Release 20210906*:
129
+ funcname: special case functools.partial, return concise name.
130
+
131
+ *Release 20210717*:
132
+ * Move @trace from debug to py.func, defer log call imports to avoid loops.
133
+ * Drop cs.pfx requirement, import opportunisticly with fallback.
134
+
135
+ *Release 20200518*:
136
+ funcname: prefer `func.__qualname__` over `func.__name__`
137
+
138
+ *Release 20190729*:
139
+ funccite: handle callables with no __code__ attribute.
140
+
141
+ *Release 20190108*:
142
+ Break import loop. Use cs.py3.raise_from for portability.
143
+
144
+ *Release 20181231*:
145
+ * Some type specific convenience wrappers for yields_type and returns_type.
146
+ * Bugfix for @prop.
147
+
148
+ *Release 20170906.1*:
149
+ Minor tweaks.
150
+
151
+ *Release 20170906*:
152
+ Bugfix for @prop.
153
+
154
+ *Release 20170608*:
155
+ New decorator @prop which works just like the builtin @property, but transmutes internal AttributeError into RuntimeError, unmasking many bugs.
156
+
157
+ *Release 20160828*:
158
+ Use "install_requires" instead of "requires" in DISTINFO.
159
+
160
+ *Release 20150115*:
161
+ First PyPI release.
@@ -0,0 +1,5 @@
1
+ cs/py/func.py,sha256=vUqCtqYJi6qlFUZZRyRanMt7DcgYxZVxVLI5SCDuuq4,8255
2
+ cs.py.func-20220619.dist-info/METADATA,sha256=LikrQehY1CiKfeKcbXIZhqA5FoSvP44nTxsmGkd6iDg,5012
3
+ cs.py.func-20220619.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
4
+ cs.py.func-20220619.dist-info/top_level.txt,sha256=MJn10B_hUOb4f5hIJP5wKj_mMYsOQ6NUWqo9vSLmwcQ,3
5
+ cs.py.func-20220619.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.37.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ cs