If I transform a program into an AST graph, could I directly modify the AST such that a function that the program calls (but doesn't define) is wrapped by a decorator?
Here is an example with refactor:
import refactor
from refactor import context
class AddDecorator(refactor.Rule):
context_providers = (context.Ancestry,)
decorators = {
'call_a': 'decorator_a'
}
def match(self, node):
# check if this is a call to a function specified in decorators
assert isinstance(node, ast.Call)
assert isinstance(node.func, ast.Name)
assert node.func.id in self.decorators
decorator_id = self.decorators[node.func.id]
# check whether it is already wrapped to a decorator or not
parent = self.context['ancestry'].get_parent(node)
if isinstance(parent, ast.Call) and isinstance(parent.func, ast.Name):
assert parent.func.id != decorator_id
decorator_call = ast.Call(
ast.Name(decorator_id, ast.Load()),
args=[node],
keywords=[]
)
return refactor.ReplacementAction(node, decorator_call)
session = refactor.Session(rules=[AddDecorator])
print(session.run("""
from x import call_a, call_b, call_c, decorator_a
#decorator_a
def call_d():
# already decorated
print(decorator_a(call_a()))
# simple call
z = call_a()
return 'something'
# complex call
q = call_c(
call_d(),
call_a(),
call_b()
)
some_others
"""))
It will produce the following code which wraps all call_a()s with decorator_a() if they are not already wrapped;
from x import call_a, call_b, call_c, decorator_a
#decorator_a
def call_d():
# already decorated
print(decorator_a(call_a()))
# simple call
z = decorator_a(call_a())
return 'something'
# complex call
q = call_c(
call_d(),
decorator_a(call_a()),
call_b()
)
some_others
First, I am using python 3.6.
I am trying import and use my own .py file in my project. I import my LinkedList.py file and create a Mylist class, which extends the imported file's class.
When I try the construct an instance of the Mylist class, which involves creating an instance of my inheritedLinkedList derived class, I get the following error:
Traceback (most recent call last):
File "*/PycharmProjects/Veri Yapilari/lists.py", line 65, in <module>
test = Mylist()
File "*/PycharmProjects/Veri Yapilari/lists.py", line 38, in __init__
self.linkedlist = inheritedLinkedList()
File "*/PycharmProjects/Veri Yapilari/lists.py", line 8, in __init__
super.__init__()
TypeError: descriptor '__init__' of 'super' object needs an argument
Here's the section of the code where the problem occurs:
test = Mylist()
test.insertFirstM(incomingDataM=4) # <- Causes a TypeError.
Below is the main script in its entirety:
import LinkedList as myLinkedList
class inheritedLinkedList(myLinkedList.DoublyLinkedList):
def __init__(self):
super.__init__()
def raplaceElements(self, dataToBeChanged, incomingData):
position = self.find(dataToBeChanged)
position.data = incomingData
def swapElements(self, swap1, swap2):
position1 = self.find(swap1)
prev1 = position1.previous
next1 = position1.next
position2 = self.find(swap2)
prev2 = position2.previous
next2 = position2.next
prev1.next = position1
position1.previous = prev1
position1.next = next1
next1.previous = position1
prev2.next = position2
position2.previous = prev2
position2.next = next2
next2.previous = position2
def insertBefore(self, incomingData, previousNode=None):
self.insert(incomingData, self.find(previousNode).previous.data)
class Mylist:
def __init__(self):
# self.linkedlist = inheritedLinkedList;
self.linkedlist = inheritedLinkedList() # Per martineau's suggestion.
def replaceElements(self, dataToBeChanged, incomingData):
self.linkedlist.raplaceElements(dataToBeChanged, incomingData)
def swapElements(self, swap1, swap2):
self.linkedlist.swapElements(swap1, swap2)
def insertFirstM(self, incomingDataM):
self.linkedlist.insertFirst(incomingDataM)
def insertLast(self, incomingData):
self.linkedlist.insert(incomingData)
def insertAfter(self, incomingData, incomingNode):
self.linkedlist.insert(incomingData, incomingNode)
def insertBefore(self, incomingData, incomingNode):
self.linkedlist.insert(incomingData, incomingNode)
def remove(self, incomingData):
self.linkedlist.remove(incomingData)
def listprint(self):
self.linkedlist.listprint()
test = Mylist()
test.insertFirstM(4)
The code for the imported LinkedList module (LinkedList.py) can be obtained—if needed—by downloading it from my github repository.
As I said in a comment, you're not using the super built-in correctly. Try do things this way instead (so it's like the example in the linked documentation):
class inheritedLinkedList(myLinkedList.DoublyLinkedList):
def __init__(self):
super().__init__() # Change line to this.
Actually, since the derived class' __init__() is currently doing nothing but that, it's not even necessary because that's what would occur automatically if the subclass didn't define its own. In other words, the following would accomplish the same thing:
class inheritedLinkedList(myLinkedList.DoublyLinkedList):
# ** NOT REALLY NEEDED AT ALL **
# def __init__(self):
# super().__init__()
P.S. You also ought to change the very end of the LinkedList.py script so the last few lines that are there don't execute when it's imported as a module by lists.py:
...
nextNode.previous = previousNode
dataToBeDeleted.next = dataToBeDeleted.previous = None
if __name__ == '__main__': # Should add this, too.
list1 = SinglyLinkedList()
list2 = DoublyLinkedList()
list2.insertFirst(6)
Hi next thing is bothering me:
I'm trying to use the next class:
class GameStatus(object):
"""Enum of possible Game statuses."""
__init__ = None
NotStarted, InProgress, Win, Lose = range(4)
def getStatus(number):
return{
0: "NotStarted",
1: "InProgress",
2: "Win",
3: "Lose",
}
in another class(both in same py file).
In this another class in his method init i do next thing:
class Game(object):
"""Handles a game of minesweeper by supplying UI to Board object."""
gameBoard = []
gs = ''
def __init__(self, board):
self.gameBoard = board
gs = GameStatus() //THIS IS THE LINE
And when i try to run the game i get next error message:
File "C:\Users\Dron6\Desktop\Study\Python\ex6\wp-proj06.py", line 423, in __init__
gs = GameStatus()
TypeError: 'NoneType' object is not callable
What am i doing wrong?
You are setting the GameStatus initializer to None:
class GameStatus(object):
__init__ = None
Don't do that. Python expects that to be a method. If you do not want to have a __init__ method, do not specify it at all. At most, make it an empty function:
class GameStatus(object):
def __init__(self, *args, **kw):
# Guaranteed to do nothing. Whatsoever. Whatever arguments you pass in.
pass
If you wanted to create an enum-like object, take a look at How can I represent an 'Enum' in Python?
For Python 2.7, you could use:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
reverse = dict((value, key) for key, value in enums.iteritems())
enums['reverse_mapping'] = reverse
return type('Enum', (), enums)
GameStatus = enum('NotStarted', 'InProgress', 'Win', 'Lose')
print GameStatus.NotStarted # 0
print GameStatus.reverse_mapping[0] # NotStarted
Ok so after small research i found the problem.
The code i got was:
class GameStatus(object):
"""Enum of possible Game statuses."""
__init__ = None
NotStarted, InProgress, Win, Lose = range(4)
I needed to convert nymbers to their value.
So i build:
def getStatus(number):
return{
0: "NotStarted",
1: "InProgress",
2: "Win",
3: "Lose",
}
And couldnt use it, because i couldn't create an object, and this mothod wasn't static.
The solution: Add #staticmethod before the method.
Plus i had one small error with the return switch, the correct version which works is:
#staticmethod
def getStatus(number):
return{
0: "NotStarted",
1: "InProgress",
2: "Win",
3: "Lose",
}[number]
Thanks for all who tried to help.
The following code:
class ParentModel(models.Model):
pass
class ChildA(ChildB):
pass
class ChildB(ParentModel):
pass
Obviously fails with the message.
NameError: name "ChildB" is not defined
Is there anyway to get around this issue, without actually reordering the class definitions? (The code is auto-generated, about 45K lines, and the order of classes is random).
Perfectionists look away!!
This is a workaround (hack); the solution would be to solve the incorrect declaration order.
WARNING: This is extremely daft.
Concept:
Imagine a namespace where anything can exist. Literally anything that is asked of it. Not the smartest thing usually but out-of-order declaration isn't smart either, so why not?
The key problem of out-of-sequence classes is that dependent classes were being defined before their dependencies, the base classes. At that point of evaluation, the base classes are undefined resulting in a NameError.
Wrapping each class in try except statements would take as much effort as rewriting the module anyway, so that can be dismissed out of hand.
A more efficient (in terms of programmer time) means of suppressing NameError must be used. This can be achieved by making the namespace totally permissible, as in, if a lookup object doesn't exist, it should be created thereby avoiding a NameError. This is the obvious danger of this approach as a lookup becomes a creation.
Implementation:
Namespaces in Python are dictionaries, I believe, and dictionaries methods can be overloaded, including the lookup function: __getitem__. So mr_agreeable is a dictionary subclass with an overloaded __getitem__ method which automatically creates a blank class when a lookup key doesn't exist. An instance of mr_agreeable is passed to execfile as the namespace for the classes.py script. The objects (aside from the builtins) created execfile call are merged with the globals() dict of the calling script: hack.py.
This works because Python doesn't care if class' base classes are changed after the fact.
This may be implementation dependent, I don't know. Tested on: Python 2.7.3 64bit on Win7 64bit.
Assuming your out-of-order classes are defined in classes.py:
class ParentModel(object):
name = "parentmodel"
class ChildC(ChildA):
name = "childc"
class ChildA(ChildB):
name = "childa"
class ChildB(ParentModel):
name = "childb"
The loader script, lets call it hack.py:
from random import randint
from codecs import encode
class mr_agreeable(dict):
sin_counter = 0
nutty_factor = 0
rdict = {0 : (0, 9), 200 : (10, 14), 500 : (15, 16), 550 : (17, 22)}
def __getitem__(self, key):
class tmp(object):
pass
tmp.__name__ = key
if(not key in self.keys()):
self.prognosis()
print self.insanity()
return self.setdefault(key, tmp)
def prognosis(self):
self.sin_counter += 1
self.nutty_factor = max(filter(lambda x: x < self.sin_counter, self.rdict.keys()))
def insanity(self):
insane_strs = \
[
"Nofbyhgryl", "Fher, jul abg?", "Sbe fher", "Fbhaqf terng", "Qrsvangryl", "Pbhyqa'g nterr zber",
"Jung pbhyq tb jebat?", "Bxl Qbnxl", "Lrc", "V srry gur fnzr jnl", "Zneel zl qnhtugre",
"Znlor lbh fubhyq svk gung", "1 AnzrReebe vf bar gbb znal naq n 1000'f abg rabhtu", "V'ir qbar qvegvre guvatf",
"Gur ebbz vf fgnegvat gb fcva", "Cebonoyl abg", "Npghnyyl, ab ..... nyevtug gura", "ZNXR VG FGBC",
"BU TBQ AB", "CYRNFR AB", "LBH'ER OERNXVAT CLGUBA", "GUVF VF ABG PBAFRAGHNY", "V'Z GRYYVAT THVQB!!"
]
return encode("ze_nterrnoyr: " + insane_strs[randint(*self.rdict[self.nutty_factor])], "rot13")
def the_act():
ns = mr_agreeable()
execfile("classes.py", ns)
hostages = list(set(ns.keys()) - set(["__builtins__", "object"]))
globals().update([(key, ns[key]) for key in hostages])
the_act()
mr_agreeable acts as the permissible namespace to the complied classes.py. He reminds you this is bad form.
My previous answer showed a loader script that executed the out of order script in execfile but provided a dynamic name space that created placeholder classes (these are typically base classes sourced before they are defined). It then loaded the changes from this name space in the loader's global namespace.
This approach has two problems:
1) Its a hack
2) The assumed class of the placeholders is the object class. So when:
class ChildC(ChildA):
name = "childc"
is evaluated, the namespace detects ChildA is undefined and so creates a placeholder class (an object subclass). When ChildA is actually defined (in the out-of-order script), it might be of a different base class than object and so rebasing ChildC to the new ChildA will fail if ChildA's base is not the object class (what ChildC was originally created with). See this for more info.
So I created a new script, which actually rewrites the input out-of-order script using a similar concept to the previous hack and this script. The new script is used by calling:
python mr_agreeable.py -i out_of_order.py -o ordered.py
mr_agreeable.py:
import os
import sys
from codecs import encode
from random import randint
import getopt
import inspect
import types
__doc__ = \
'''
A python script that re-orders out of sequence class defintions
'''
class rebase_meta(type):
'''
Rebase metaclass
Automatically rebases classes created with this metaclass upon
modification of classes base classes
'''
org_base_classes = {}
org_base_classes_subs = {}
base_classes = {}
base_classes_subs = {}
mod_loaded = False
mod_name = ""
mod_name_space = {}
def __init__(cls, cls_name, cls_bases, cls_dct):
#print "Making class: %s" % cls_name
super(rebase_meta, cls).__init__(cls_name, cls_bases, cls_dct)
# Remove the old base sub class listings
bases = rebase_meta.base_classes_subs.items()
for (base_cls_name, sub_dict) in bases:
sub_dict.pop(cls_name, None)
# Add class to bases' sub class listings
for cls_base in cls_bases:
if(not rebase_meta.base_classes_subs.has_key(cls_base.__name__)):
rebase_meta.base_classes_subs[cls_base.__name__] = {}
rebase_meta.base_classes[cls_base.__name__] = cls_base
rebase_meta.base_classes_subs[cls_base.__name__][cls_name] = cls
# Rebase the sub classes to the new base
if(rebase_meta.base_classes.has_key(cls_name)): # Is class a base class
subs = rebase_meta.base_classes_subs[cls_name]
rebase_meta.base_classes[cls_name] = cls # Update base class dictionary to new class
for (sub_cls_name, sub_cls) in subs.items():
if(cls_name == sub_cls_name):
continue
sub_bases_names = [x.__name__ for x in sub_cls.__bases__]
sub_bases = tuple([rebase_meta.base_classes[x] for x in sub_bases_names])
try:
# Attempt to rebase sub class
sub_cls.__bases__ = sub_bases
#print "Rebased class: %s" % sub_cls_name
except TypeError:
# The old sub class is incompatible with the new base class, so remake the sub
if(rebase_meta.mod_loaded):
new_sub_cls = rebase_meta(sub_cls_name, sub_bases, dict(sub_cls.__dict__.items() + [("__module__", rebase_meta.mod_name)]))
rebase_meta.mod_name_space[sub_cls_name] = new_sub_cls
else:
new_sub_cls = rebase_meta(sub_cls_name, sub_bases, dict(sub_cls.__dict__.items()))
subs[sub_cls_name] = new_sub_cls
#classmethod
def register_mod(self, imod_name, imod_name_space):
if(not self.mod_loaded):
self.org_base_classes = self.base_classes.copy()
self.org_base_classes_subs = self.base_classes_subs.copy()
self.mod_loaded = True
else:
self.base_classes = self.org_base_classes
self.base_classes_subs = self.org_base_classes_subs
self.mod_name = imod_name
self.mod_name_space = imod_name_space
# Can't subclass these classes
forbidden_subs = \
[
"bool",
"buffer",
"memoryview",
"slice",
"type",
"xrange",
]
# Builtin, sub-classable classes
org_class_types = filter(lambda x: isinstance(x, type) and (not x.__name__ in forbidden_subs) and x.__module__ == "__builtin__", types.__builtins__.values())
# Builtin classes recreated with Rebasing metaclass
class_types = [(cls.__name__, rebase_meta(cls.__name__, (cls,), {})) for cls in org_class_types]
# Overwrite builtin classes
globals().update(class_types)
class mr_quiet(dict):
'''
A namespace class that creates placeholder classes upon
a non existant lookup. mr_quiet doesnt say much.
'''
def __getitem__(self, key):
if(not key in self.keys()):
if(hasattr(__builtins__, key)):
return getattr(__builtins__, key)
else:
if(not key in self.keys()):
self.sanity_check()
return self.setdefault(key, rebase_meta(key, (object,), {}))
else:
return dict.__getitem__(self, key)
def sanity_check(self):
pass
class mr_agreeable(mr_quiet):
'''
A talkative cousin of mr_quiet.
'''
sin_counter = 0
nutty_factor = 0
rdict = {0 : (0, 9), 200 : (10, 14), 500 : (15, 16), 550 : (17, 22)}
def sanity_check(self):
self.prognogsis()
print self.insanity()
def prognogsis(self):
self.sin_counter += 1
self.nutty_factor = max(filter(lambda x: x < self.sin_counter, self.rdict.keys()))
def insanity(self):
insane_strs = \
[
"Nofbyhgryl", "Fher, jul abg?", "Sbe fher", "Fbhaqf terng", "Qrsvangryl", "Pbhyqa'g nterr zber",
"Jung pbhyq tb jebat?", "Bxl Qbnxl", "Lrc", "V srry gur fnzr jnl", "Zneel zl qnhtugre",
"Znlor lbh fubhyq svk gung", "1 AnzrReebe vf bar gbb znal naq n 1000'f abg rabhtu", "V'ir qbar qvegvre guvatf",
"Gur ebbz vf fgnegvat gb fcva", "Cebonoyl abg", "Npghnyyl, ab ..... nyevtug gura", "ZNXR VG FGBC",
"BU TBQ AB", "CYRNFR AB", "LBH'ER OERNXVAT CLGUBA", "GUVF VF ABG PBAFRAGHNY", "V'Z GRYYVAT THVQB!!"
]
return encode("ze_nterrnoyr: " + insane_strs[randint(*self.rdict[self.nutty_factor])], "rot13")
def coll_up(ilist, base = 0, count = 0):
'''
Recursively collapse nested lists at depth base and above
'''
tlist = []
if(isinstance(ilist, __builtins__.list) or isinstance(ilist, __builtins__.tuple)):
for q in ilist:
tlist += coll_up(q, base, count + 1)
else:
if(base > count):
tlist = ilist
else:
tlist = [ilist]
return [tlist] if((count != 0) and (base > count)) else tlist
def build_base_dict(ilist):
'''
Creates a dictionary of class : class bases pairs
'''
base_dict = {}
def build_base_dict_helper(iclass, idict):
idict[iclass] = list(iclass.__bases__)
for x in iclass.__bases__:
build_base_dict_helper(x, idict)
for cur_class in ilist:
build_base_dict_helper(cur_class, base_dict)
return base_dict
def transform_base_to_sub(idict):
'''
Transforms a base dict into dictionary of class : sub classes pairs
'''
sub_dict = {}
classes = idict.keys()
for cur_class in idict:
sub_dict[cur_class] = filter(lambda cls: cur_class in idict[cls], classes)
return sub_dict
recur_class_helper = lambda idict, ilist = []: [[key, recur_class_helper(idict, idict[key])] for key in ilist]
recur_class = lambda idict: recur_class_helper(idict, idict.keys())
class proc_func(list):
'''
Cmdline processing class
'''
def __init__(self, name = "", *args, **kwargs):
self.name = name
super(list, self).__init__(*args, **kwargs)
def get_args(self, *args):
self.extend(filter(lambda x: x, args))
def __call__(self, *args):
print self.name
print self
class proc_inputs(proc_func):
def get_args(self, *args):
self.extend(filter(os.path.isfile, args))
class proc_outputs(proc_func):
pass
class proc_helper(proc_func):
'''
Help function
Print help information
'''
def get_args(self, *args):
self()
def __call__(self, *args):
print __file__
print __doc__
print "Help:\n\t%s -h -i inputfile -o ouputfile" % sys.argv[0]
print "\t\t-h or --help\tPrint this help message"
print "\t\t-i or --input\tSpecifies the input script"
print "\t\t-o or --output\tSpecifies the output script"
sys.exit()
if __name__ == "__main__":
proc_input = proc_inputs("input")
proc_output = proc_outputs("output")
proc_help = proc_helper("help")
cmd_line_map = \
{
"-i" : proc_input,
"--input" : proc_input,
"-o" : proc_output,
"--ouput" : proc_output,
"-h" : proc_help,
"--help" : proc_help
}
try:
optlist, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input=", "output="])
for (key, value) in optlist:
cmd_line_map[key].get_args(value)
except getopt.GetoptError:
proc_help()
if(len(proc_input) != len(proc_output)):
print "Input files must have a matching output file"
proc_help()
elif(not proc_input):
proc_help()
else:
in_out_pairs = zip(proc_input, proc_output)
for (in_file, out_file) in in_out_pairs:
dodgy_module_name = os.path.splitext(in_file)[0]
sys.modules[dodgy_module_name] = types.ModuleType(dodgy_module_name)
sys.modules[dodgy_module_name].__file__ = in_file
# Make a fake space post haste
name_space = mr_agreeable\
(
[
("__name__", dodgy_module_name), # Needed for the created classes to identify with the fake module
("__module__", dodgy_module_name), # Needed to fool the inspect module
] + \
class_types
)
# Exclude these from returning
exclusions = name_space.keys()
# Associate the fake name space to the rebasing metaclass
rebase_meta.register_mod(dodgy_module_name, name_space)
# Run dodgy code
execfile(in_file, name_space)
# Bring back dodgy classes
import_classes = [cls if(isinstance(cls, type) and not cls_name in exclusions) else None for (cls_name, cls) in name_space.items()]
dodgy_import_classes = filter(lambda x: x, import_classes)
# Create base and sub class dictionaries
base_dict = build_base_dict(dodgy_import_classes)
sub_dict = transform_base_to_sub(base_dict)
# Create sets of base and sub classes
base_set = reduce(lambda x, y: x | y, map(set, base_dict.values()), set([]))
sub_set = reduce(lambda x, y: x | y, map(set, sub_dict.values()), set([]))
kings = list(base_set - sub_set) # A list of bases which are not subs
kingdoms = recur_class_helper(sub_dict, kings) # A subclass tree of lists
lineages = coll_up(kingdoms, 2) # Flatten the tree branches at and below 2nd level
# Filter only for the clases created in the dodgy module
inbred_lines = [filter(lambda x: x.__module__ == dodgy_module_name, lineage) for lineage in lineages]
# Load Source
for lineage in inbred_lines:
for cls in lineage:
setattr(cls, "_source", inspect.getsource(cls))
# Write Source
with open(out_file, "w") as file_h:
for lineage in inbred_lines:
for cls in lineage:
file_h.write(cls._source + "\n")