Skip to content

Commit b90f796

Browse files
author
Sylvain MARIE
committed
Added support for python 2. Fixes #11
1 parent a536635 commit b90f796

File tree

8 files changed

+157
-81
lines changed

8 files changed

+157
-81
lines changed

code_generation/goodies_template.mako

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# ----
22
# This file is generated by mini_lambda_methods_generation.py - do not modify it !
33
# ----
4+
from __future__ import print_function
45
from mini_lambda.main import C, make_lambda_friendly_class, make_lambda_friendly_method
56
# from warnings import warn
67

code_generation/mini_lambda_methods_generation.py

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,16 @@
1414
from ordered_set import OrderedSet
1515

1616
from inspect import getmembers
17-
from autoclass import autoclass
18-
from enforce import runtime_validation
1917

2018

21-
@runtime_validation
22-
@autoclass
2319
class Override:
24-
def __init__(self, method_name: str,
25-
# we cannot use Callable, see https://github.com/RussBaz/enforce/issues/58
26-
unbound_method: Optional[Any] = None,
27-
pair_operator: Optional[str] = None, is_operator_left: bool = True,
28-
uni_operator: Optional[str] = None,
29-
precedence_level: Optional[str] = None
20+
def __init__(self,
21+
method_name, # type: str
22+
unbound_method=None, # type: Optional[Any]
23+
pair_operator=None, # type: Optional[str]
24+
is_operator_left=True, # type: bool
25+
uni_operator=None, # type: Optional[str]
26+
precedence_level=None # type: Optional[str]
3027
):
3128
"""
3229
A descriptor for a method to override in _InputEvaluatorGenerated.
@@ -43,9 +40,14 @@ def __init__(self, method_name: str,
4340
:param is_operator_left:
4441
:param uni_operator: for self-operator e.g. -x
4542
:param precedence_level: the precedence level of the operation. This is the string representation of the
46-
constant in mini_lambda base.py
43+
constant in mini_lambda base.py
4744
"""
48-
pass
45+
self.method_name = method_name
46+
self.unbound_method = unbound_method
47+
self.pair_operator = pair_operator
48+
self.is_operator_left = is_operator_left
49+
self.uni_operator = uni_operator
50+
self.precedence_level = precedence_level
4951

5052
def __hash__(self):
5153
return hash(self.method_name)
@@ -62,13 +64,12 @@ def __str__(self):
6264
return '{}: Standard default'.format(self.method_name)
6365

6466

65-
@runtime_validation
66-
@autoclass
6767
class OverExc:
68-
def __init__(self, method_name: str,
69-
module_method_name: Optional[str] = None,
70-
# we cannot use Callable, see https://github.com/RussBaz/enforce/issues/58
71-
unbound_method: Optional[Any] = None):
68+
def __init__(self,
69+
method_name, # type: str
70+
module_method_name=None, # type: Optional[str]
71+
unbound_method=None # type: Optional[Any]
72+
):
7273
"""
7374
A descriptor for a method to override with exception in _InputEvaluatorGenerated, and for which a module-level
7475
replacement method needs to be provided.
@@ -84,23 +85,30 @@ def __init__(self, method_name: str,
8485
:param module_method_name:
8586
:param unbound_method:
8687
"""
87-
# this is executed AFTER @autoargs
88+
self.method_name = method_name
8889
self.module_method_name = module_method_name or method_name.replace('__', '').capitalize()
90+
self.unbound_method = unbound_method
8991

9092
def __hash__(self):
9193
return hash(self.method_name)
9294

9395
def __str__(self):
94-
return 'Exception {} replaced by module method {}'.format(self.method_name, self.module_method_name)
96+
return 'Exception {} replaced with module method {}'.format(self.method_name, self.module_method_name)
9597

9698

97-
@runtime_validation
98-
@autoclass
9999
class Goodie:
100-
def __init__(self, item_name: str, function_name: Optional[str] = None,
101-
constant_name: Optional[str] = None, class_name: Optional[str] = None,
102-
import_line: Optional[str] = ''):
103-
pass
100+
def __init__(self,
101+
item_name, # type: str
102+
function_name=None, # type: Optional[str]
103+
constant_name=None, # type: Optional[str]
104+
class_name=None, # type: Optional[str]
105+
import_line='' # type: Optional[str]
106+
):
107+
self.item_name = item_name
108+
self.function_name = function_name
109+
self.constant_name = constant_name
110+
self.class_name = class_name
111+
self.import_line = import_line
104112

105113
def __str__(self):
106114
if self.function_name:
@@ -189,7 +197,8 @@ def __get_all_magic_methods(*classes):
189197
return {name for clazz in classes for name in dir(clazz) if name.startswith('__')}
190198

191199

192-
def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
200+
def define_what_needs_to_be_written():
201+
# type: (...) -> Tuple[Set[Override], Set[OverExc]]
193202
"""
194203
Creates three sets containing the definition of what we want to write as methods in the generated class.
195204
:return: a tuple of two sorted sets. The first set contains Override definitions, the second one OverExc
@@ -218,6 +227,8 @@ def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
218227
# .__next__
219228
# to_override.update(__get_all_magic_methods(Iterator, Generator))
220229
to_override.add(Override('__next__', unbound_method=next))
230+
# to support Python 2
231+
to_override.add(Override('next', unbound_method=next))
221232

222233
# ** Initializable Object **
223234
# .__new__, .__init__, .__del__
@@ -304,7 +315,7 @@ def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
304315
# ** Numeric types **
305316
# .__add__, .__radd__, .__sub__, .__rsub__, .__mul__, .__rmul__, .__truediv__, .__rtruediv__,
306317
# .__mod__, .__rmod__, .__divmod__, .__rdivmod__, .__pow__, .__rpow__
307-
# .__matmul__, .__floordiv__, .__rfloordiv__
318+
# .__matmul__, .__div__, .__rdiv__, .__floordiv__, .__rfloordiv__
308319
# .__lshift__, .__rshift__, __rlshift__, __rrshift__
309320
# .__neg__, .__pos__, .__abs__, .__invert__
310321
# to_override.update(__get_all_magic_methods(Integral))
@@ -322,8 +333,15 @@ def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
322333
to_override.add(Override('__rdivmod__'))
323334
to_override.add(Override('__pow__', pair_operator='**', precedence_level='_PRECEDENCE_EXPONENTIATION'))
324335
to_override.add(Override('__rpow__', pair_operator='**', is_operator_left=False, precedence_level='_PRECEDENCE_EXPONENTIATION'))
325-
to_override.add(Override('__matmul__', pair_operator='@', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
336+
337+
# does not work in python 2
338+
# to_override.add(Override('__matmul__', pair_operator='@', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
339+
to_override.add(Override('__matmul__', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
340+
326341
# Override('__rmatmul__', operator='@', is_operator_left=False),
342+
# for python 2
343+
to_override.add(Override('__div__', pair_operator='/', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
344+
to_override.add(Override('__rdiv__', pair_operator='/', is_operator_left=False, precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
327345
to_override.add(Override('__floordiv__', pair_operator='//', precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
328346
to_override.add(Override('__rfloordiv__', pair_operator='//', is_operator_left=False, precedence_level='_PRECEDENCE_MUL_DIV_ETC'))
329347
to_override.add(Override('__lshift__', pair_operator='<<', precedence_level='_PRECEDENCE_SHIFTS'))
@@ -336,17 +354,20 @@ def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
336354
to_override.add(Override('__pos__', uni_operator='+', precedence_level='_PRECEDENCE_POS_NEG_BITWISE_NOT'))
337355
to_override.add(Override('__abs__', unbound_method=abs))
338356
to_override.add(Override('__invert__', uni_operator='~', precedence_level='_PRECEDENCE_POS_NEG_BITWISE_NOT'))
339-
to_override.add(Override('__round__', unbound_method=round))
357+
# requires __float__ in python 2: skip
358+
# to_override.add(Override('__round__', unbound_method=round))
340359

341360
# ** Boolean types **
342361
# .__and__, .__xor__, .__or__, __rand__, __rxor__, __ror__
343362
to_skip.update({'__and__', '__xor__', '__or__', '__rand__', '__rxor__', '__ror__'})
344363

345364
# ** Type conversion **
346365
# __int__, __long__, __float__, __complex__, __oct__, __hex__, __index__, __trunc__, __coerce__
347-
to_override.add(Override('__trunc__'))
366+
from math import trunc
367+
to_override.add(Override('__trunc__', unbound_method=trunc))
348368
to_override.add(Override('__coerce__'))
349369
to_skip.update({'__index__'})
370+
to_override_with_exception.add(OverExc('__round__', unbound_method=round))
350371
to_override_with_exception.add(OverExc('__int__', unbound_method=int))
351372
# OverExc('__long__', unbound_method=long),
352373
to_override_with_exception.add(OverExc('__float__', unbound_method=float))
@@ -363,32 +384,34 @@ def define_what_needs_to_be_written() -> Tuple[Set[Override], Set[OverExc]]:
363384
to_override_2 = OrderedSet()
364385
for overriden in to_override:
365386
if overriden not in to_skip and overriden not in to_override_with_exception:
366-
assert type(overriden) == Override
387+
assert isinstance(overriden, Override)
367388
to_override_2.add(overriden)
368389

369390
to_override_with_exception_2 = OrderedSet()
370391
for overriden_with_e in to_override_with_exception:
371392
if overriden_with_e not in to_skip:
372-
assert type(overriden_with_e) == OverExc
393+
assert isinstance(overriden_with_e, OverExc)
373394
to_override_with_exception_2.add(overriden_with_e)
374395

375396
return to_override_2, to_override_with_exception_2
376397

377398

378-
def define_goodies() -> Tuple[List[str], List[Goodie]]:
399+
def define_goodies():
400+
# type: (...) -> Tuple[List[str], List[Goodie]]
379401
"""
380402
381403
:return:
382404
"""
383405
packages = [math, decimal]
384-
built_in_functions = ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr',
406+
built_in_functions = ['abs', 'all', 'any', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr',
385407
'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate',
386-
'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
387-
'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len',
408+
'eval', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
409+
'hash', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len',
388410
'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord',
389411
'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
390412
'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
391-
413+
# these do not work in python 2
414+
protected_functions = ['ascii', 'help', 'exec']
392415
import_list = list()
393416
goodies_list = list()
394417

code_generation/mini_lambda_template.mako

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# ----
22
# This file is generated by mini_lambda_methods_generation.py - do not modify it !
33
# ----
4-
from typing import Any
4+
try: # python 3.5+
5+
from typing import Any
6+
except ImportError:
7+
pass
58

9+
from math import trunc
610
from mini_lambda.base import _LambdaExpressionBase, evaluate, get_repr, FunctionDefinitionError, \
711
_get_root_var
812
from mini_lambda.base import _PRECEDENCE_ADD_SUB, _PRECEDENCE_MUL_DIV_ETC, _PRECEDENCE_COMPARISON, \
@@ -69,7 +73,7 @@ class _LambdaExpressionGenerated(_LambdaExpressionBase):
6973

7074
## -----------------------------
7175
% else:
72-
## --------pairwise operator - left---------------------
76+
## --------pairwise operator - right ---------------------
7377
def ${o.method_name}(self, other):
7478
""" Returns a new _LambdaExpression performing 'other ${o.pair_operator} <r>' on the result <r> of this evaluator's evaluation """
7579
## def _${o.method_name}(r, input):
@@ -105,11 +109,11 @@ class _LambdaExpressionGenerated(_LambdaExpressionBase):
105109

106110
# return a new LambdaExpression of the same type than self, with the new function as inner function
107111
# Note: we use precedence=None for coma-separated items inside the parenthesis
108-
string_expr = '${o.unbound_method.__name__}(' + get_repr(self, None) \
109-
+ (', ' if (len(args) > 0 and len(kwargs) > 0) else '') \
110-
+ ', '.join([get_repr(arg, None) for arg in args]) \
111-
+ ', '.join([arg_name + '=' + get_repr(arg, None) for arg_name, arg in kwargs.items()]) \
112-
+ ')'
112+
string_expr = ('${o.unbound_method.__name__}(' + get_repr(self, None)
113+
+ (', ' if (len(args) > 0 and len(kwargs) > 0) else '')
114+
+ ', '.join([get_repr(arg, None) for arg in args])
115+
+ ', '.join([arg_name + '=' + get_repr(arg, None) for arg_name, arg in kwargs.items()])
116+
+ ')')
113117
return type(self)(fun=_${o.method_name}, precedence_level=_PRECEDENCE_SUBSCRIPTION_SLICING_CALL_ATTRREF,
114118
str_expr=string_expr, root_var=root_var)
115119

mini_lambda/base.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class FunctionDefinitionError(Exception):
3333
""" An exception thrown when defining a function incorrectly """
3434

3535

36-
class _LambdaExpressionBase:
36+
class _LambdaExpressionBase(object):
3737
"""
3838
A _LambdaExpressionBase is a wrapper for a function (self._fun) with a SINGLE argument.
3939
It can be evaluated on any input by calling the 'evaluate' method. This will execute self._fun() on this input.
@@ -48,8 +48,14 @@ class _LambdaExpressionBase:
4848

4949
__slots__ = ['_fun', '_str_expr', '_root_var', '_precedence_level']
5050

51-
def __init__(self, str_expr: str=None, is_constant: bool=False, constant_value: Any=None,
52-
precedence_level: int=None, fun: Callable=None, root_var=None):
51+
def __init__(self,
52+
str_expr=None, # type: str
53+
is_constant=False, # type: bool
54+
constant_value=None, # type: Any
55+
precedence_level=None, # type: int
56+
fun=None, # type: Callable
57+
root_var=None
58+
):
5359
"""
5460
Constructor with an optional nested evaluation function. If no argument is provided, the nested evaluation
5561
function is the identity function with one single parameter x
@@ -131,7 +137,10 @@ def to_string(self):
131137
"""
132138
return self._str_expr
133139

134-
def assert_has_same_root_var(self, other: Any) -> Any:
140+
def assert_has_same_root_var(self,
141+
other # type: Any
142+
):
143+
# type: (...) -> Any
135144
"""
136145
Asserts that if other is also a _LambdaExpressionBase, then it has the same root variable.
137146
It returns the root variable to use for expressions combining self and other.
@@ -200,7 +209,11 @@ def evaluate_inner_function_and_apply_object_method(raw_input):
200209
root_var=root_var)
201210

202211
@classmethod
203-
def constant(cls, value: T, name: str = None) -> Union[T, Any]: # Any really means _LambdaExpressionBase
212+
def constant(cls,
213+
value, # type: T
214+
name=None # type: str
215+
):
216+
# type: (...) -> Union[T, _LambdaExpressionBase]
204217
"""
205218
Creates a constant expression. This is useful when
206219
* you want to use a method on an object that is not an expression, as in 'toto'.prefix(x) where x is an
@@ -279,7 +292,8 @@ def evaluate_all_and_apply_method(input):
279292
str_expr=string_expr, root_var=root_var)
280293

281294

282-
def _get_root_var(*args, **kwargs) -> Tuple[Any, _LambdaExpressionBase]:
295+
def _get_root_var(*args, **kwargs):
296+
# type: (...) -> Tuple[Any, _LambdaExpressionBase]
283297
"""
284298
Returns the root variable to use when the various arguments are used in the same expression, or raises an exception
285299
if two arguments have incompatible root variables
@@ -304,7 +318,8 @@ def _get_root_var(*args, **kwargs) -> Tuple[Any, _LambdaExpressionBase]:
304318
return root_var, first_expression
305319

306320

307-
def evaluate(statement: Any, input):
321+
def evaluate(statement, # type: Any
322+
input):
308323
"""
309324
A helper function to evaluate something, whether it is a _LambdaExpressionBase, a callable, or a non-callable.
310325
* if that something is not callable, it returns it directly
@@ -322,7 +337,9 @@ def evaluate(statement: Any, input):
322337
return statement
323338

324339

325-
def get_repr(statement: Any, target_precedence_level: float = None):
340+
def get_repr(statement, # type: Any
341+
target_precedence_level=None # type: float
342+
):
326343
"""
327344
A helper function to return the representation of something, whether it is a _LambdaExpressionBase,
328345
a callable, or a non-callable.

0 commit comments

Comments
 (0)