Porting of Pyidp to Pyidp3

  • Firstly Python’s own 2to3 was used for the initial port. This is a tool to automagically convert from Python2 to Python3. This produces a log of the changes made, which can be found at the bottom of this page.
  • The way popen works was changed to only accept byte objects (and it doesn’t convert them automatically), so I had make sure all input was encoded first, and all output was decoded afterwards. This can be done easily by using the encode() and decode() methods.

That’s it. Using these two simple tricks, I was able to convert Pyidp to work on Python3 (Doctors hate him!). After porting, features were added, QOL was improved and bugs were squashed. More on that can be found at Pyidp3 features.

--- idpobjects.py	(original)
+++ idpobjects.py	(refactored)
@@ -55,7 +55,7 @@
 
     def product_of_types(self):
         import itertools
-        types = map(lambda x: getattr(self.idp,x), self.typing)      
+        types = [getattr(self.idp,x) for x in self.typing]      
         return itertools.product(*types)
 
 class IDPEnumeratedObject(IDPVocabularyObject):
@@ -96,7 +96,7 @@
 
     def product_of_types(self):
         import itertools
-        types = map(lambda x: getattr(self.idp,x), self.typing)      
+        types = [getattr(self.idp,x) for x in self.typing]      
         return itertools.product(*types)
 
     @property
@@ -162,7 +162,7 @@
         IDPGeneratedObject.__init__(self,idp,name,args,impl)
     
     def __getitem__(self, key):
-        args = map(self.idp.object_for_name, key)
+        args = list(map(self.idp.object_for_name, key))
         try:
             res = self.implementation(args)
         except AttributeError:
@@ -275,4 +275,4 @@
         self.rules = rule_list
 
     def in_theory(self):
-        return "{\n" + "\n".join(map(lambda x: x.in_theory(), self.rules)) + "\n}"
+        return "{\n" + "\n".join([x.in_theory() for x in self.rules]) + "\n}"
--- idp_parse_out.py	(original)
+++ idp_parse_out.py	(refactored)
@@ -126,7 +126,7 @@
 def parse_tuple(tup):
     tup = tup.strip()
     elements = tup.split(',')
-    parsed = map(parse_element, elements)
+    parsed = list(map(parse_element, elements))
     if len(parsed) == 1:
         return parsed[0]
     return tuple(parsed)
@@ -144,7 +144,7 @@
 def parse_enumeration(s):
     s = s.strip()
     elements = s.split(';')
-    parsed = map(parse_enumerated, elements)
+    parsed = list(map(parse_enumerated, elements))
     if "->" in s: # Function
         return dict(parsed)
     else: # Predicate
@@ -153,7 +153,7 @@
 def parse_range(s):
     s = s.strip()
     low, up = s.split('..')
-    return range(int(low),int(up))
+    return list(range(int(low),int(up)))
 
 def parse_contents(s):
     stripped = s.strip().lstrip('{').rstrip('}').strip()
--- idp_py_syntax.py	(original)
+++ idp_py_syntax.py	(refactored)
@@ -44,7 +44,7 @@
         self.formula = formula
 
     def __str__(self):
-        var_tuple =  flatten(map(lambda x: x[0], self.vars))
+        var_tuple =  flatten([x[0] for x in self.vars])
         it = (" ".join(var_tuple) + ": " +
                 " & ".join(map(tuple_to_atom, self.vars)))
         if self.agg == "card":
@@ -63,10 +63,10 @@
             symbols =  "+-*%/^"
             return "(" + x + ")" if any([(s in x) for s in symbols]) else x
         if self.symbol == "/":
-            (l,r) = map(lambda x: add_pars(str(x)), self.children)
+            (l,r) = [add_pars(str(x)) for x in self.children]
             return "(" + l + "-" + l + "%" + r + ") / " + r 
         else:
-            return (" " + self.symbol + " ").join(map(lambda x: add_pars(str(x)), self.children))
+            return (" " + self.symbol + " ").join([add_pars(str(x)) for x in self.children])
     
 class BooleanFormula(Formula):
 
@@ -75,7 +75,7 @@
         self.children = children
 
     def __str__(self):
-        return (" " + self.symbol + " ").join(map(lambda x: "(" + str(x) + ")", self.children))
+        return (" " + self.symbol + " ").join(["(" + str(x) + ")" for x in self.children])
 
 class UnaryFormula(Formula):
 
@@ -100,7 +100,7 @@
             return "&"
 
     def __str__(self):
-        var_tuple =  flatten(map(lambda x: x[0], self.vars))
+        var_tuple =  flatten([x[0] for x in self.vars])
         return (self.kind + " " + " ".join(var_tuple) + ": " +
                 " & ".join(map(tuple_to_atom, self.vars)) +
                 self.guard_sym() + " "+ str(self.formula))
@@ -179,7 +179,7 @@
         return UnaryFormula(symb, self.visit(node.operand))
 
     def visit_Tuple(self, node):
-        return tuple(map(lambda x: self.visit(x), node.elts))
+        return tuple([self.visit(x) for x in node.elts])
 
     def visit_GeneratorExp(self,node):
         return self.visit_ListComp(node)
@@ -199,7 +199,7 @@
              symb = "?" if func == "any" else "!"         
              return QuantifiedFormula(symb, *self.visit(node.args[0]))
          aggregates = { 'sum' : 'sum', 'len' : 'card', 'product' : 'prod', 'max' : 'max', 'min' : 'min' }
-         if func in aggregates.keys():
+         if func in list(aggregates.keys()):
              return AggregateFormula(aggregates[func], *self.visit(node.args[0]))
          return str(func) + "(" + ", ".join(map(str, [self.visit(arg) for arg in node.args])) + ")"
 
--- test.py	(original)
+++ test.py	(refactored)
@@ -1,11 +1,11 @@
 #!/usr/bin/python3
-from typedIDP import *
+from .typedIDP import *
 
 
 idp = IDP('/home/saltfactory/Documents/Masterproef/IDP/idp3-3.7.1-Linux/usr/local/bin/idp')
 
-idp.Type("Student", range(int(5)))
-idp.Type("Groepnummer",range(int(1)))
+idp.Type("Student", list(range(int(5))))
+idp.Type("Groepnummer",list(range(int(1))))
 idp.Function("InGroep(Student): Groepnummer")
 idp.Constraint("""!groep[Groepnummer]: #{student[Student]:
                          InGroep(student) = groep} =< """ + str(10),True)
--- typedIDP.py	(original)
+++ typedIDP.py	(refactored)
@@ -1,8 +1,9 @@
 IDP_LOCATION = "/home/saltfactory/Documents/Masterproef/IDP/idp3-3.7.1-Linux/usr/local/bin/idp"
 
-from idp_py_syntax import parse_formula
-
-from idpobjects import *
+from .idp_py_syntax import parse_formula
+
+from .idpobjects import *
+from functools import reduce
 
 class Block(object):
 
@@ -56,7 +57,7 @@
         return "vocabulary " + self.name
 
 def subclasses(cls):
-    return reduce(lambda x,y: x + y, map(lambda x: subclasses(x) + [x], cls.__subclasses__()), [])
+    return reduce(lambda x,y: x + y, [subclasses(x) + [x] for x in cls.__subclasses__()], [])
 
 class IDP(object):
 
@@ -103,7 +104,7 @@
     #if the second argument 'enumeration' is given, then the Type is an int
     #else, it's left blank
     def Type(self, name, enumeration):
-        if len(enumeration) > 0 and all([isinstance(x, (int,int)) for x in enumeration]):
+        if len(enumeration) > 0 and all([isinstance(x, int) for x in enumeration]):
             res = IDPIntType(self, name, enumeration)
         else:
             res = IDPType(self, name, enumeration)
@@ -166,7 +167,7 @@
         return str(thing)
     
     def assign_name(self, object_):
-        if isinstance(object_, (int,int,str,bool,float)): # Primitive type
+        if isinstance(object_, (int,str,bool,float)): # Primitive type
             return object_
         name = "o" + str(id(object_))
         self.object_names[name] = object_
@@ -222,7 +223,7 @@
         self.know(old_class(old.typedName, new))
 
     def __str__(self):
-        return "\n".join(map(lambda bl: bl.show(self.idpobjects.values()), self.blocks)) + "\n" + IDP.gen_models + "\n"
+        return "\n".join([bl.show(list(self.idpobjects.values())) for bl in self.blocks]) + "\n" + IDP.gen_models + "\n"
 
     def fillIn(self, pred):
         if self.dirty:
@@ -272,13 +273,13 @@
             print("GOT OUTPUT:")
             print(out)
             print("END OF IDP OUTPUT")
-        import idp_parse_out
+        from . import idp_parse_out
         if out.strip() == "nil":
             print("UNSATISFIABLE!")
             self.cache = {'satisfiable' : []}
         else:
 #            self.Predicate("satisfiable", [()])
-            self.cache = idp_parse_out.idp_parse(out, map(lambda x: x.name, self.wanted))
+            self.cache = idp_parse_out.idp_parse(out, [x.name for x in self.wanted])
         self.dirty = False
 
     def checkSat(self):
@@ -297,8 +298,8 @@
         out,err = idp.communicate(input=str(script))
 
         if __debug__:
-            print("err:" + err)
-            print("out:" + out)
+            print(("err:" + err))
+            print(("out:" + out))
         #check wether the output is true or false
         if out.find("true") != -1:
             return True
@@ -343,7 +344,7 @@
 def type(idp):
     def foo(cls):
         clsname = cls.__name__
-        for name, method in cls.__dict__.iteritems():
+        for name, method in cls.__dict__.items():
             if hasattr(method, "_idp_return_type"):
                 rt = getattr(method, "_idp_return_type")
                 if isinstance(rt, str):