Reputation: 622
Say I have this function a()
,
C = some-constant
def b()
....
def a():
... do something with b() ...
... do something with C ...
Is there a way for me to know, given the function a
object, its dependencies which, in this case, are function b()
and variable C
.
I am aware that the latter part might be harder to get, and is less consistent, so if you have an answer for either part, I would be glad to hear.
Upvotes: 2
Views: 1147
Reputation: 11
I just created a function/system for doing this, because my library was getting a bit too complicated for my little brain :P
Disclaimer: I'm not 100% sure if this works, but it seems to work.
It's slightly hacky because you do have to do some initial work.
You start with a dictionary with as keys, the names of functions/imports/anything, and as values, a list names of the things that the key depends on. Like this:
inputD = {
'obj1':['obj2', 'obj3'],
'obj2':[],
'obj3':['obj7','obj8'],
'obj7':[],
'obj8':[]
}
each object only 'knows' what it needs directly. obj1 is perhaps a function that requires the import 'obj2' and calls the function 'obj3'. obj2 is immediately usable without prerequisites, obj3 requires obj7 and obj8, which in turn can be used without prerequisites.
I have a core function, 'addDependencies', and one that wraps around that function, 'returnDependencies', to make my life easier. I'll first show the core function alone.
def returnDependencies(inputD, key):
# blabla some stuff I will show later down below
dependencies = []
def addDependencies(d, k2):
# deps is the list of things that k2 depends on
deps = d[k2]
# convenience, for adding something to the dependencies list
def add(k3):
if k3 not in dependencies:
dependencies.append(k3)
# checks if each element of 'sub' is in 'full'. returns True if it is, else returns False
def allIn(full, sub):
for i in sub:
if i not in full:
return False
return True
# basically checks if the requirements for this key (k2) have been met.
# if len(deps) is zero, it has no requirements, so it can be added to 'dependencies'.
# if each element of deps is already in 'dependencies', the requirements are met, so it can be added.
# if the requirements aren't met, it calls this function on each of its dependencies, to make sure the requirements 'underneath' are met first.
# so if you start with obj1, this first if statement would be False, and it would call addDependencies and pass 'obj2' first, which would get added to 'dependencies', then pass obj3, which requires obj7 and obj8, so it would add those first to 'dependencies', then it would add obj3.
# then finally it can add itself, so 'obj1'.
if len(deps) == 0 or allIn(dependencies, deps):
add(k2)
else:
for k1 in deps:
addDependencies(inputD, k1)
addDependencies(inputD, k2)
addDependencies(inputD, key)
return dependencies
print(returnDependencies(inputD, 'obj1'))
will return
['obj2', 'obj7', 'obj8', 'obj3', 'obj1']
And I added 2 small things to make it easier to write inputD
.
def returnDependencies(inputD, key):
# this first loop lets you put all independent objects in a single key named 'independents' in inputD. so for functions that use the standard library, and imports.
# like this: 'inputD = {'independents':['obj2','obj7','obj8'],<the rest>}'
newDict = {}
for k,v in inputD.items():
if k == 'independents':
for item in v:
newDict[item] = []
else:
newDict[k] = v
# if anything is missing from inputD, will abort and tell you what's missing.
allKeys = newDict.keys()
missing = []
for k,v in newDict.items():
for item in v:
if item not in allKeys:
if item not in missing:
missing.append(item)
if len(missing) != 0:
exit(f"missing: {missing}")
# then do the rest as normal, except for 1) now you call addDependencies on newDict instead of inputD. And 2) You have to change deps = d[k2] to deps = newDict[k2] inside addDependencies
def addDependencies(d, k2):
# stuff
addDependencies(newDict, key)
return dependencies
Putting it all together.
def returnDependencies(inputD, inputK):
# this first loop lets you put all independent objects in a single key named 'independents' in inputD. so for functions that use the standard library, and imports.
# like this: 'inputD = {'independents':['obj2','obj7','obj8'],<the rest>}'
newDict = {}
for k,v in inputD.items():
if k == 'independents':
for item in v:
newDict[item] = []
else:
newDict[k] = v
# if anything is missing from inputD, will abort and tell you what's missing.
allKeys = newDict.keys()
missing = []
for k,v in newDict.items():
for item in v:
if item not in allKeys:
if item not in missing:
missing.append(item)
if len(missing) != 0:
exit(f"missing: {missing}")
# then do the rest as normal, except now you call addDependencies on newDict instead of inputD
dependencies = []
def addDependencies(d, k2):
# deps is the list of things that k2 depends on
deps = newDict[k2]
# convenience, for adding something to the dependencies list
def add(k3):
if k3 not in dependencies:
dependencies.append(k3)
# checks if each element of 'sub' is in 'full'. returns True if it is, else returns False
def allIn(full, sub):
for i in sub:
if i not in full:
return False
return True
# basically checks if the requirements for this key (k2) have been met.
# if len(deps) is zero, it has no requirements, so it can be added.
# if each element of deps is already in 'dependencies', the requirements are met, so it can be added.
# if the requirements aren't met, it calls this function on each of its dependencies, to make sure the requirements 'underneath' are met first.
# so if you start with obj1, this first if statement would be False, and it would call addDependencies and pass 'obj2' first, which would get added to 'dependencies', then pass obj3, which requires obj7 and obj8, so it would add those first to 'dependencies', then it would add obj3.
# then finally it can add itself, so 'obj1'.
if len(deps) == 0 or allIn(dependencies, deps):
add(k2)
else:
for k1 in deps:
addDependencies(inputD, k1)
addDependencies(inputD, k2)
addDependencies(newDict, inputK)
return dependencies
inputD = {
'independents':['obj2','obj7','obj8'],
'obj1':['obj2', 'obj3'],
'obj3':['obj7','obj8'],
}
print(returnDependencies(inputD, 'obj1'))
This will print
['obj2', 'obj7', 'obj8', 'obj3', 'obj1']
And if you follow the logic of the function, you will see why it's in that specific order.
Upvotes: 1