Skip to content

Commit 865a9b1

Browse files
committed
1.1.0 - Compatibility with standard functions
* It is now possible to use any function in a lambda expression, through use of the `make_lambda_friendly_...` methods * All `built-in` methods as well as all constants, methods and classes from the `math.py` and `decimal.py` modules are provided in a lambda-friendly way by the package for convenience * Updated documentation accordingly, and made main page clearer * Renamed class `_InputEvaluator` into `_LambdaExpression` * A few bugfixes in particular support for keyword arguments when a function call is made in a lambda expression * `<expr>.nnot()`, `<expr>.any()` and `<expr>.all()` renamed `<expr>.not_()`, `<expr>.any_()` and `<expr>.all_()` for consistency and to avoid conflicts with any()/all() methods that would already be defined in the class, for example NumPy. base.py: - StackableFunctionEvaluator renamed _LambdaExpressionBase. - Added the notion of constant expression - Added support of keyword arguments in all 'add_.._methods' - Added make_lambda_friendly_method and utility method _get_expr_or_result_for_method which becomes the single conversion method for all non-class methods code generation: - Support for constant expressions in all generated methods - Added support of keyword arguments - _get_expr_or_result_for_method is used everywhere as possible - added missing method __round__ - New template for pre-converted constants/functions/classes, generating goodies_generated.py main.py - Support for constant expressions - Added support of keyword arguments in __call__ - Added __format__ as a special methods - Added .is_in() - Added make_lambda_friendly_class and Constant() (alias C()) - Added another alias for _() : F() - Get and Slice are simplified now that they use _get_expr_or_result_for_method - Any/All removed as they are now directly part of the generated methods in goodies_generated.py - * `<expr>.nnot()`, `<expr>.any()` and `<expr>.all()` renamed `<expr>.not_()`, `<expr>.any_()` and `<expr>.all_()` for consistency and to avoid conflicts with any()/all() methods that would already be defined in the class, for example NumPy. goodies.py - Added a few precreated variables
1 parent 8c51656 commit 865a9b1

15 files changed

+2405
-771
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# ----
2+
# This file is generated by mini_lambda_methods_generation.py - do not modify it !
3+
# ----
4+
from mini_lambda.base import make_lambda_friendly_method
5+
from mini_lambda.main import C, make_lambda_friendly_class
6+
7+
% for o in import_lines:
8+
${o}
9+
% endfor
10+
11+
% for o in to_create:
12+
% if o.constant_name:
13+
${o.item_name} = C(${o.constant_name}, '${o.constant_name}')
14+
% elif o.function_name:
15+
${o.item_name} = make_lambda_friendly_method(${o.function_name}, '${o.function_name}')
16+
% else:
17+
## CLASS
18+
${o.item_name} = make_lambda_friendly_class(${o.class_name})
19+
% endif
20+
21+
22+
% endfor

code_generation/mini_lambda_methods_generation.py

Lines changed: 130 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1+
from inspect import isclass
12
from sys import getsizeof
2-
from typing import Tuple, Set, Optional, Any
3+
from typing import Tuple, Set, Optional, Any, List
34

45
import os
6+
import math
57

68
# from quik import Template
9+
import decimal
710
from mako import exceptions
811
from mako.template import Template
912

1013
# from sortedcontainers import SortedSet
1114
from ordered_set import OrderedSet
1215

13-
from autoclass import autoclass
16+
from autoclass import autoclass, getmembers
1417
from enforce import runtime_validation
1518

1619

@@ -27,7 +30,7 @@ def __init__(self, method_name: str,
2730
"""
2831
A descriptor for a method to override in _InputEvaluatorGenerated.
2932
* If only method_name is provided, the overriden method adds itself to the stack
30-
(see StackableFunctionEvaluator)
33+
(see _LambdaExpressionBase)
3134
* If unbound_method is provided, the overriden method adds the provided unbound method to the stack
3235
* If operator and is_operator_left are provided, the overriden method adds a method performing
3336
(args <operator> x) if is_operator_left=False or (x <operator> args) if is_operator_left=True.
@@ -70,7 +73,7 @@ def __init__(self, method_name: str,
7073
replacement method needs to be provided.
7174
7275
The method_name will raise an exception and indicate that module_method_name is the replacement method. The
73-
latter will implement by adding method_name to the stack (see StackableFunctionEvaluator). If unbound_method is
76+
latter will implement by adding method_name to the stack (see _LambdaExpressionBase). If unbound_method is
7477
provided, the module method will add unbound_method to the stack instead of method_name.
7578
7679
By default module_method_name is equal to a capitalized, underscores-removed, version of the method name.
@@ -90,36 +93,79 @@ def __str__(self):
9093
return 'Exception {} replaced by module method {}'.format(self.method_name, self.module_method_name)
9194

9295

96+
@runtime_validation
97+
@autoclass
98+
class Goodie:
99+
def __init__(self, item_name: str, function_name: Optional[str] = None,
100+
constant_name: Optional[str] = None, class_name: Optional[str] = None):
101+
pass
102+
103+
def __str__(self):
104+
if self.function_name:
105+
return 'Function {} replaced with {}'.format(self.function_name, self.item_name)
106+
elif self.constant_name:
107+
return 'Constant {} replaced with {}'.format(self.constant_name, self.item_name)
108+
else:
109+
return 'Class {} replaced with {}'.format(self.class_name, self.item_name)
110+
111+
93112
def generate_code():
94113
"""
95114
This method reads the template file, fills it with the appropriate contents, and writes the result in the
96115
mini_lambda_generated.py file. All contents of the destination file are overriden by this operation.
97116
:return:
98117
"""
99118

100-
# generate the to-do list
119+
# (1) generate the to-do list for the first template
101120
to_override, to_override_with_exception = define_what_needs_to_be_written()
121+
102122
# check outside of the template that it works:
103123
for o in to_override:
104124
print(o)
105125
for o in to_override_with_exception:
106126
print(o)
107127

128+
# generate
129+
generate_from_template('mini_lambda_template.mako', 'generated.py',
130+
dict(to_override=to_override, to_override_with_exception=to_override_with_exception))
131+
132+
# (2) to-do list for the second template
133+
import_lines, to_create = define_goodies()
134+
135+
# check outside of the template that it works:
136+
for o in import_lines:
137+
print(o)
138+
for o in to_create:
139+
print(o)
140+
141+
# generate
142+
generate_from_template('goodies_template.mako', 'goodies_generated.py',
143+
dict(import_lines=import_lines, to_create=to_create))
144+
145+
146+
def generate_from_template(src_file, dst_file, template_vars):
147+
"""
148+
Routine to open a source template file, load the variables inside and dump the result in the destination file
149+
150+
:param dst_file:
151+
:param src_file:
152+
:param template_vars:
153+
:return:
154+
"""
108155
# open the mako template file
109156
THIS_DIR = os.path.dirname(__file__)
110-
template_file = os.path.join(THIS_DIR, 'mini_lambda_template.mako')
157+
template_file = os.path.join(THIS_DIR, src_file)
111158
with open(template_file) as f:
112159
body = f.read()
113-
114160
try:
115161
# create the template
116162
temp = Template(text=body) # , module_directory=os.path.join(THIS_DIR, 'tmp'))
117163

118164
# fill it with the variables contents
119-
res = temp.render(to_override=to_override, to_override_with_exception=to_override_with_exception)
165+
res = temp.render(**template_vars)
120166

121167
# write the result to the destination file
122-
dest_file = os.path.join(THIS_DIR, os.pardir, 'mini_lambda', 'generated.py')
168+
dest_file = os.path.join(THIS_DIR, os.pardir, 'mini_lambda', dst_file)
123169
with open(dest_file, 'wt') as f:
124170
f.write(res)
125171
except:
@@ -177,18 +223,19 @@ def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
177223
to_override_with_exception.add(OverExc('__str__', unbound_method=str))
178224
to_override_with_exception.add(OverExc('__repr__', unbound_method=repr))
179225
to_override_with_exception.add(OverExc('__bytes__', unbound_method=bytes))
226+
# this is a special case
180227
to_override_with_exception.add(OverExc('__format__', unbound_method=format))
181228
to_override_with_exception.add(OverExc('__sizeof__', unbound_method=getsizeof))
182229

183230
# ** Comparable Objects **
184231
# .__lt__, .__le__, .__eq__, .__ne__, .__gt__, .__ge__
185232
# to_override.update(__get_all_magic_methods(Set))
186-
to_override.add(Override('__lt__', pair_operator='<', precedence_level='PRECEDENCE_COMPARISON'))
187-
to_override.add(Override('__le__', pair_operator='<=', precedence_level='PRECEDENCE_COMPARISON'))
188-
to_override.add(Override('__eq__', pair_operator='==', precedence_level='PRECEDENCE_COMPARISON'))
189-
to_override.add(Override('__ne__', pair_operator='!=', precedence_level='PRECEDENCE_COMPARISON'))
190-
to_override.add(Override('__gt__', pair_operator='>', precedence_level='PRECEDENCE_COMPARISON'))
191-
to_override.add(Override('__ge__', pair_operator='>=', precedence_level='PRECEDENCE_COMPARISON'))
233+
to_override.add(Override('__lt__', pair_operator='<', precedence_level='_PRECEDENCE_COMPARISON'))
234+
to_override.add(Override('__le__', pair_operator='<=', precedence_level='_PRECEDENCE_COMPARISON'))
235+
to_override.add(Override('__eq__', pair_operator='==', precedence_level='_PRECEDENCE_COMPARISON'))
236+
to_override.add(Override('__ne__', pair_operator='!=', precedence_level='_PRECEDENCE_COMPARISON'))
237+
to_override.add(Override('__gt__', pair_operator='>', precedence_level='_PRECEDENCE_COMPARISON'))
238+
to_override.add(Override('__ge__', pair_operator='>=', precedence_level='_PRECEDENCE_COMPARISON'))
192239

193240
# ** Hashable Object **
194241
# .__hash__
@@ -255,34 +302,35 @@ def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
255302
# .__lshift__, .__rshift__, __rlshift__, __rrshift__
256303
# .__neg__, .__pos__, .__abs__, .__invert__
257304
# to_override.update(__get_all_magic_methods(Integral))
258-
to_override.add(Override('__add__', pair_operator='+', precedence_level='PRECEDENCE_ADD_SUB'))
259-
to_override.add(Override('__radd__', pair_operator='+', is_operator_left=False, precedence_level='PRECEDENCE_ADD_SUB'))
260-
to_override.add(Override('__sub__', pair_operator='-', precedence_level='PRECEDENCE_ADD_SUB'))
261-
to_override.add(Override('__rsub__', pair_operator='-', is_operator_left=False, precedence_level='PRECEDENCE_ADD_SUB'))
262-
to_override.add(Override('__mul__', pair_operator='*', precedence_level='PRECEDENCE_MUL_DIV_ETC'))
263-
to_override.add(Override('__rmul__', pair_operator='*', is_operator_left=False, precedence_level='PRECEDENCE_MUL_DIV_ETC'))
264-
to_override.add(Override('__truediv__', pair_operator='/', precedence_level='PRECEDENCE_MUL_DIV_ETC'))
265-
to_override.add(Override('__rtruediv__', pair_operator='/', is_operator_left=False, precedence_level='PRECEDENCE_MUL_DIV_ETC'))
266-
to_override.add(Override('__mod__', pair_operator='%', precedence_level='PRECEDENCE_MUL_DIV_ETC'))
267-
to_override.add(Override('__rmod__', pair_operator='%', is_operator_left=False, precedence_level='PRECEDENCE_MUL_DIV_ETC'))
305+
to_override.add(Override('__add__', pair_operator='+', precedence_level='_PRECEDENCE_ADD_SUB'))
306+
to_override.add(Override('__radd__', pair_operator='+', is_operator_left=False, precedence_level='_PRECEDENCE_ADD_SUB'))
307+
to_override.add(Override('__sub__', pair_operator='-', precedence_level='_PRECEDENCE_ADD_SUB'))
308+
to_override.add(Override('__rsub__', pair_operator='-', is_operator_left=False, precedence_level='_PRECEDENCE_ADD_SUB'))
309+
to_override.add(Override('__mul__', pair_operator='*', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
310+
to_override.add(Override('__rmul__', pair_operator='*', is_operator_left=False, precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
311+
to_override.add(Override('__truediv__', pair_operator='/', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
312+
to_override.add(Override('__rtruediv__', pair_operator='/', is_operator_left=False, precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
313+
to_override.add(Override('__mod__', pair_operator='%', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
314+
to_override.add(Override('__rmod__', pair_operator='%', is_operator_left=False, precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
268315
to_override.add(Override('__divmod__'))
269316
to_override.add(Override('__rdivmod__'))
270-
to_override.add(Override('__pow__', pair_operator='**', precedence_level='PRECEDENCE_EXPONENTIATION'))
271-
to_override.add(Override('__rpow__', pair_operator='**', is_operator_left=False, precedence_level='PRECEDENCE_EXPONENTIATION'))
272-
to_override.add(Override('__matmul__', pair_operator='@', precedence_level='PRECEDENCE_MUL_DIV_ETC'))
317+
to_override.add(Override('__pow__', pair_operator='**', precedence_level='_PRECEDENCE_EXPONENTIATION'))
318+
to_override.add(Override('__rpow__', pair_operator='**', is_operator_left=False, precedence_level='_PRECEDENCE_EXPONENTIATION'))
319+
to_override.add(Override('__matmul__', pair_operator='@', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
273320
# Override('__rmatmul__', operator='@', is_operator_left=False),
274-
to_override.add(Override('__floordiv__', pair_operator='//', precedence_level='PRECEDENCE_MUL_DIV_ETC'))
275-
to_override.add(Override('__rfloordiv__', pair_operator='//', is_operator_left=False, precedence_level='PRECEDENCE_MUL_DIV_ETC'))
276-
to_override.add(Override('__lshift__', pair_operator='<<', precedence_level='PRECEDENCE_SHIFTS'))
277-
to_override.add(Override('__rlshift__', pair_operator='<<', is_operator_left=False, precedence_level='PRECEDENCE_SHIFTS'))
278-
to_override.add(Override('__rshift__', pair_operator='>>', precedence_level='PRECEDENCE_SHIFTS'))
279-
to_override.add(Override('__rrshift__', pair_operator='>>', is_operator_left=False, precedence_level='PRECEDENCE_SHIFTS'))
280-
to_override.add(Override('__rshift__', pair_operator='>>', precedence_level='PRECEDENCE_SHIFTS'))
281-
to_override.add(Override('__rshift__', pair_operator='>>', precedence_level='PRECEDENCE_SHIFTS'))
282-
to_override.add(Override('__neg__', uni_operator='-', precedence_level='PRECEDENCE_POS_NEG_BITWISE_NOT'))
283-
to_override.add(Override('__pos__', uni_operator='+', precedence_level='PRECEDENCE_POS_NEG_BITWISE_NOT'))
321+
to_override.add(Override('__floordiv__', pair_operator='//', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
322+
to_override.add(Override('__rfloordiv__', pair_operator='//', is_operator_left=False, precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
323+
to_override.add(Override('__lshift__', pair_operator='<<', precedence_level='_PRECEDENCE_SHIFTS'))
324+
to_override.add(Override('__rlshift__', pair_operator='<<', is_operator_left=False, precedence_level='_PRECEDENCE_SHIFTS'))
325+
to_override.add(Override('__rshift__', pair_operator='>>', precedence_level='_PRECEDENCE_SHIFTS'))
326+
to_override.add(Override('__rrshift__', pair_operator='>>', is_operator_left=False, precedence_level='_PRECEDENCE_SHIFTS'))
327+
to_override.add(Override('__rshift__', pair_operator='>>', precedence_level='_PRECEDENCE_SHIFTS'))
328+
to_override.add(Override('__rshift__', pair_operator='>>', precedence_level='_PRECEDENCE_SHIFTS'))
329+
to_override.add(Override('__neg__', uni_operator='-', precedence_level='_PRECEDENCE_POS_NEG_BITWISE_NOT'))
330+
to_override.add(Override('__pos__', uni_operator='+', precedence_level='_PRECEDENCE_POS_NEG_BITWISE_NOT'))
284331
to_override.add(Override('__abs__', unbound_method=abs))
285-
to_override.add(Override('__invert__', uni_operator='~', precedence_level='PRECEDENCE_POS_NEG_BITWISE_NOT'))
332+
to_override.add(Override('__invert__', uni_operator='~', precedence_level='_PRECEDENCE_POS_NEG_BITWISE_NOT'))
333+
to_override.add(Override('__round__', unbound_method=round))
286334

287335
# ** Boolean types **
288336
# .__and__, .__xor__, .__or__, __rand__, __rxor__, __ror__
@@ -321,5 +369,48 @@ def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
321369
return to_override_2, to_override_with_exception_2
322370

323371

372+
def define_goodies() -> Tuple[List[str], List[Goodie]]:
373+
"""
374+
375+
:return:
376+
"""
377+
packages = [math, decimal]
378+
built_in_functions = ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr',
379+
'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate',
380+
'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
381+
'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len',
382+
'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord',
383+
'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
384+
'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
385+
386+
import_list = list()
387+
goodies_list = list()
388+
389+
for package in packages:
390+
# import pprint
391+
# pprint(getmembers(math, callable))
392+
import_string = "from " + package.__name__ + " import"
393+
for item_name, item in getmembers(package):
394+
if not item_name.startswith('_'):
395+
import_string += ' ' + item_name + ','
396+
if isclass(item):
397+
new_class_name = item_name[0].upper() + item_name
398+
goodies_list.append(Goodie(item_name=new_class_name, class_name=item_name))
399+
elif callable(item):
400+
goodies_list.append(Goodie(item_name=item_name.capitalize(), function_name=item_name))
401+
else:
402+
new_item_name = item_name.capitalize()
403+
if new_item_name == item_name:
404+
new_item_name = new_item_name[0] + new_item_name
405+
goodies_list.append(Goodie(item_name=new_item_name, constant_name=item_name))
406+
407+
import_list.append(import_string[0:-1])
408+
409+
for function_name in built_in_functions:
410+
goodies_list.append(Goodie(item_name=function_name.capitalize(), function_name=function_name))
411+
412+
return import_list, goodies_list
413+
414+
324415
if __name__ == '__main__':
325416
generate_code()

0 commit comments

Comments
 (0)