how to call function dynamically - python

I have a class containing several lists as attributes and several add methods to append an object to a specific list based on its type.
My code reads a csv file containing the type of an object in order to create and add it to my cart.
My problem is that I'm testing the object type to call the right 'add' function using if elif syntax but this is not very nice and hard to maintain.
For example
import csv
class my_item():
def __init__(self, name):
self.name = name
class fruit(my_item):
pass
class vegetable(my_item):
pass
class meat(my_item):
pass
class fish(my_item):
pass
class shopping_cart():
def __init__(self):
self.fruits = []
self.vegetables = []
self.meat = []
self.fish = []
def add_fruit(self, o):
self.fruits.append(o)
def add_vegetable(self, o):
self.vegetables.append(o)
def add_meat(self, o):
self.meat.append(o)
def add_fish(self, o):
self.fish.append(o)
def __str__(self):
msg = ""
msg += "{:<25}= {:<5}\n".format('Total', str(len(self.fruits) + len(self.vegetables) + len(self.meat) + len(self.fish)))
for attrname in vars(self):
value = getattr(self, attrname)
if isinstance(value, list):
msg += " {:<23}= {:<5}\n".format(attrname, len(value))
return msg
def main():
input_f = 'input.csv'
my_cart = shopping_cart()
with open(input_f, 'r') as i:
rows = csv.reader(i, delimiter=';')
for row in rows:
item = globals()[row[0]](row[1])
if item.__class__.__name__ == 'fruit':
my_cart.add_fruit(item)
elif item.__class__.__name__ == 'vegetable':
my_cart.add_vegetable(item)
elif item.__class__.__name__ == 'meat':
my_cart.add_meat(item)
else:
my_cart.add_fish(item)
print (my_cart)
if __name__ == '__main__':
main()
Do you see any alternatives to the if elif block?
Thanks for your feedback.

Sure, you just need to create the function name dynamically and call it.
Be careful, this will works only if my_cart have add_{{ item name }} method.
def main():
input_f = 'input.csv'
my_cart = shopping_cart()
with open(input_f, 'r') as i:
rows = csv.reader(i, delimiter=';')
for row in rows:
item = globals()[row[0]](row[1])
item_name = item.__class__.__name__
item_add_func_name = 'add_{}'.format(item_name)
item_add_func = getattr(my_cart, item_add_func_name, None)
if item_add_func and callable(item_add_func):
item_add_func(item)
# if item.__class__.__name__ == 'fruit':
# my_cart.add_fruit(item)
# elif item.__class__.__name__ == 'vegetable':
# my_cart.add_vegetable(item)
# elif item.__class__.__name__ == 'meat':
# my_cart.add_meat(item)
# else:
# my_cart.add_fish(item)
print (my_cart)

May I suggest a simpler class design.
my_item is left as it-is and other classes fruit, vegetable etc. are removed
shopping_cart is modified such that self.items is a dictionary where the key is the name of item, fruit, vegetables, and the values are the list of those items
Then the code might look like as follows
import csv
from collections import defaultdict
class my_item:
def __init__(self, name):
self.name = name
class shopping_cart:
def __init__(self):
#Dictionary to hold items
self.items = defaultdict(list)
def add_item(self, type, name):
#Increment the count of the item by 1
self.items[type].append(name)
def __str__(self):
#Iterate through the dictionary and print all key/value pairs
msg = ""
for k,v in self.items.items():
msg += ' {}: {} '.format(k, v)
return msg.strip()
sc = shopping_cart()
sc.add_item('fruit', 'pomme')
sc.add_item('vegetable', 'dinde')
sc.add_item('meat', 'carotte')
sc.add_item('fish', 'saumon')
print(sc)
The output will look like
fruit: ['pomme'] vegetable: ['dinde'] meat: ['carotte'] fish: ['saumon']

Related

__str__ method iterating over class attributes

I have a class with attributes made of several lists (today 8 but it'll grow for sure because my code relies on a previous code that is used on prod on another project)
This class has a __str__ function to pretty print information of the list. Today the function displays information about each list but calling them namely and I don't like that because every time my class attributes changes (add / remove list) I have to update this method
I'm looking for a way to dynamically iterate over my class attributes to display information
As an example, let's imagine the class below
class shopping_cart:
def __init__(self):
self.fruits = []
self.vegetables = []
self.meat = []
self.bread = []
def __str__(self):
msg = ""
msg += "shopping cart content\n"
msg += "---------------------\n"
msg += "---fruits-----------: " + len(self.fruits) +"\n
msg += "---vegetables-------: " + len(self.vegetables) +"\n
msg += "---meat-------------: " + len(self.meat) +"\n
msg += "---bread------------: " + len(self.bread) +"\n
return msg
As you can see the maintenance cost is high. I'm looking for something more dynamic, that will look more like
class shopping_cart:
def __init__(self):
self.fruits = []
self.vegetables = []
self.meat = []
self.bread = []
def __str__(self):
msg = ""
msg += "shopping cart content\n"
msg += "---------------------\n"
for item in <list of my class attributes>:
msg += "---" + item +"-----------: " + len(item) +"\n
return msg
Easy to read and easy to maintain.
I think the one functionality you are looking for is dir and getattr. Here is the example
class shopping_cart:
def __init__(self):
self.fruits = []
self.vegetables = []
self.meat = []
self.bread = []
def __str__(self):
msg = ""
msg += "shopping cart content\n"
msg += "---------------------\n"
members = [attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("__")]
for item in members:
msg += "---" + item +"-----------: " + str(getattr(self,item) )+"\n"
return msg
You could do that in a few ways:
1 - use the __dict__ attribute of your class. That gives you a dictionary object of all the variables, and you can iterate through them
s = shopping_cart()
s.__dict__
that should output something like
{'fruits':[], 'vegetables':[], ... }
2 - You could use a dictionary of lists, instead of a bunch of lists, and iterate on the dictionary.
3 - You could also use a list of lists (e.g. [self.fruits, self.vegetables...], and iterate on that.
You could use the built-in vars() function on an instance of the class (not the class itself):
class ShoppingCart:
def __init__(self):
self.fruits = []
self.vegetables = []
self.meat = []
self.bread = []
def __str__(self):
msg = "shopping cart contents\n"
msg += "----------------------\n"
for attrname in vars(self):
value = getattr(self, attrname)
if isinstance(value, list):
msg += "---{}{}: {:2}\n".format(
attrname, '-'*(15-len(attrname)), len(value))
return msg
cart = ShoppingCart()
print(cart)
Output:
shopping cart contents
----------------------
---fruits---------: 0
---vegetables-----: 0
---meat-----------: 0
---bread----------: 0

Class inheritance type checking after pickling in Python

Is there a sure-fire way to check that the class of an object is a sub-class of the desired super?
For Example, in a migration script that I'm writing, I have to convert objects of a given type to dictionaries in a given manner to ensure two-way compatability of the data.
This is best summed up like so:
Serializable
User
Status
Issue
Test
Set
Step
Cycle
However, when I'm recursively checking objects after depickling, I receive a Test object that yields the following results:
Testing data object type:
type(data)
{type}< class'__main.Test' >
Testing Class type:
type(Test())
{type}< class'__main.Test' >
Testing object type against class type:
type(Test()) == type(data)
{bool}False
Testing if object isinstance() of Class:
isinstance(data, Test)
{bool}False
Testing if Class isinstance() of Super Class:
isinstance(Test(), Serializable)
{bool}True
Testing isinstance() of Super Class::
isinstance(data, Serializable)
{bool}False
Interestingly, it doesn't appear to have any such problem prior to pickling as it handles the creation of dictionary and integrity hash just fine.
This only crops up with depickled objects in both Pickle and Dill.
For Context, here's the code in it's native environment - the DataCache object that is pickled:
class DataCache(object):
_hash=""
_data = None
#staticmethod
def genHash(data):
dataDict = DataCache.dictify(data)
datahash = json.dumps(dataDict, sort_keys=True)
return hashlib.sha256(datahash).digest()
#staticmethod
def dictify(data):
if isinstance(data,list):
datahash = []
for item in data:
datahash.append(DataCache.dictify(item))
elif isinstance(data,(dict, collections.OrderedDict)):
datahash = collections.OrderedDict()
for key,value in datahash.iteritems():
datahash[key]= DataCache.dictify(value)
elif isinstance(data, Serializable):
datahash = data.toDict()
else:
datahash = data
return datahash
def __init__(self, restoreDict = {}):
if restoreDict:
self.__dict__.update(restoreDict)
def __getinitargs__(self):
return (self.__dict__)
def set(self, data):
self._hash = DataCache.genHash(data)
self._data = data
def verify(self):
dataHash = DataCache.genHash(self._data)
return (self._hash == dataHash)
def get(self):
return self._data
Finally, I know there's arguments for using JSON for readability in storage, I needed Pickle's ability to convert straight to and from Objects without specifying the object type myself. (thanks to the nesting, it's not really feasible)
Am I going mad here or does pickling do something to the class definitions?
EDIT:
Minimal Implementation:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
from aenum import Enum
import json # _tricks
import base64
import argparse
import os
import sys
import datetime
import dill
import hashlib
import collections
class Serializable(object):
def __init__(self, initDict={}):
if initDict:
self.__dict__.update(initDict)
def __str__(self):
return str(self.sortSelf())
def sortSelf(self):
return collections.OrderedDict(sorted(self.__dict__.items()))
def toDict(self):
return self.__dict__
def fromDict(self, dict):
# Not using __dict__.update(...) to avoid polluting objects with the excess data
varMap = self.__dict__
if dict and varMap:
for key in varMap:
if (key in dict):
varMap[key] = dict[key]
self.__dict__.update(varMap)
return self
return None
class Issue(Serializable):
def __init__(self, initDict={}):
self.id = 0
self.key = ""
self.fields = {}
if initDict:
self.__dict__.update(initDict)
Serializable.__init__(self)
def fieldToDict(self, obj, key, type):
if key in obj:
result = obj[key]
else:
return None
if result is None:
return None
if isinstance(result, type):
return result.toDict()
return result
def fromDict(self, jsonDict):
super(Issue, self).fromDict(jsonDict)
self.fields["issuetype"] = IssueType().fromDict(self.fields["issuetype"])
self.fields["assignee"] = User().fromDict(self.fields["assignee"])
self.fields["creator"] = User().fromDict(self.fields["creator"])
self.fields["reporter"] = User().fromDict(self.fields["reporter"])
return self
def toDict(self):
result = super(Issue, self).toDict()
blankKeys = []
for fieldName, fieldValue in self.fields.iteritems():
if fieldValue is None:
blankKeys.append(fieldName)
if blankKeys:
for key in blankKeys:
self.fields.pop(key, None)
result["fields"]["issuetype"] = self.fieldToDict(result["fields"], "issuetype", IssueType)
result["fields"]["creator"] = self.fieldToDict(result["fields"], "creator", User)
result["fields"]["reporter"] = self.fieldToDict(result["fields"], "reporter", User)
result["fields"]["assignee"] = self.fieldToDict(result["fields"], "assignee", User)
return result
class IssueType(Serializable):
def __init__(self):
self.id = 0
self.name = ""
def toDict(self):
return {"id": str(self.id)}
class Project(Serializable):
def __init__(self):
Serializable.__init__(self)
self.id = 0
self.name = ""
self.key = ""
class Cycle(Serializable):
def __init__(self):
self.id = 0
self.name = ""
self.totalExecutions = 0
self.endDate = ""
self.description = ""
self.totalExecuted = 0
self.started = ""
self.versionName = ""
self.projectKey = ""
self.versionId = 0
self.environment = ""
self.totalCycleExecutions = 0
self.build = ""
self.ended = ""
self.name = ""
self.modifiedBy = ""
self.projectId = 0
self.startDate = ""
self.executionSummaries = {'executionSummary': []}
class Step(Serializable):
def __init__(self):
self.id = ""
self.orderId = 0
self.step = ""
self.data = ""
self.result = ""
self.attachmentsMap = {}
def toDict(self):
dict = {}
dict["step"] = self.step
dict["data"] = self.data
dict["result"] = self.result
dict["attachments"] = []
return dict
class Status(Serializable):
def __init__(self):
self.id = 0
self.name = ""
self.description = ""
self.isFinal = True
self.color = ""
self.isNative = True
self.statusCount = 0
self.statusPercent = 0.0
class User(Serializable):
def __init__(self):
self.displayName = ""
self.name = ""
self.emailAddress = ""
self.key = ""
self.active = False
self.timeZone = ""
class Execution(Serializable):
def __init__(self):
self.id = 0
self.orderId = 0
self.cycleId = -1
self.cycleName = ""
self.issueId = 0
self.issueKey = 0
self.projectKey = ""
self.comment = ""
self.versionId = 0,
self.versionName = "",
self.executedOn = ""
self.creationDate = ""
self.executedByUserName = ""
self.assigneeUserName = ""
self.status = {}
self.executionStatus = ""
def fromDict(self, jsonDict):
super(Execution, self).fromDict(jsonDict)
self.status = Status().fromDict(self.status)
# This is already listed as Execution Status, need to associate and convert!
return self
def toDict(self):
result = super(Execution, self).toDict()
result['status'] = result['status'].toDict()
return result
class ExecutionContainer(Serializable):
def __init__(self):
self.executions = []
def fromDict(self, jsonDict):
super(ExecutionContainer, self).fromDict(jsonDict)
self.executions = []
for executionDict in jsonDict["executions"]:
self.executions.append(Execution().fromDict(executionDict))
return self
class Test(Issue):
def __init__(self, initDict={}):
if initDict:
self.__dict__.update(initDict)
Issue.__init__(self)
def toDict(self):
result = super(Test, self).toDict()
stepField = "CustomField_0001"
if result["fields"][stepField]:
steps = []
for step in result["fields"][stepField]["steps"]:
steps.append(step.toDict())
result["fields"][stepField] = steps
return result
def fromDict(self, jsonDict):
super(Test, self).fromDict(jsonDict)
stepField = "CustomField_0001"
steps = []
if stepField in self.fields:
for step in self.fields[stepField]["steps"]:
steps.append(Step().fromDict(step))
self.fields[stepField] = {"steps": steps}
return self
class Set(Issue):
def __init__(self, initDict={}):
self.__dict__.update(initDict)
Issue.__init__(self)
class DataCache(object):
_hash = ""
_data = None
#staticmethod
def genHash(data):
dataDict = DataCache.dictify(data)
datahash = json.dumps(dataDict, sort_keys=True)
return hashlib.sha256(datahash).digest()
#staticmethod
def dictify(data):
if isinstance(data, list):
datahash = []
for item in data:
datahash.append(DataCache.dictify(item))
elif isinstance(data, (dict, collections.OrderedDict)):
datahash = collections.OrderedDict()
for key, value in datahash.iteritems():
datahash[key] = DataCache.dictify(value)
elif isinstance(data, Serializable):
datahash = data.toDict()
else:
datahash = data
return datahash
def __init__(self, restoreDict={}):
if restoreDict:
self.__dict__.update(restoreDict)
def __getinitargs__(self):
return (self.__dict__)
def set(self, data):
self._hash = DataCache.genHash(data)
self._data = data
def verify(self):
dataHash = DataCache.genHash(self._data)
return (self._hash == dataHash)
def get(self):
return self._data
def saveCache(name, projectKey, object):
filePath = "migration_caches/{projectKey}".format(projectKey=projectKey)
if not os.path.exists(path=filePath):
os.makedirs(filePath)
cache = DataCache()
cache.set(object)
targetFile = open("{path}/{name}".format(name=name, path=filePath), 'wb')
dill.dump(obj=cache, file=targetFile)
targetFile.close()
def loadCache(name, projectKey):
filePath = "migration_caches/{projectKey}/{name}".format(name=name, projectKey=projectKey)
result = False
try:
targetFile = open(filePath, 'rb')
try:
cache = dill.load(targetFile)
if isinstance(cache, DataCache):
if cache.verify():
result = cache.get()
except EOFError:
# except BaseException:
print ("Failed to load cache from file: {filePath}\n".format(filePath=filePath))
except IOError:
("Failed to load cache file at: {filePath}\n".format(filePath=filePath))
targetFile.close()
return result
testIssue = Test().fromDict({"id": 1000,
"key": "TEST",
"fields": {
"issuetype": {
"id": 1,
"name": "TestIssue"
},
"assignee": "Minothor",
"reporter": "Minothor",
"creator": "Minothor",
}
})
saveCache("Test", "TestProj", testIssue)
result = loadCache("Test", "TestProj")
EDIT 2
The script in it's current form, now seems to work correctly with vanilla Pickle, (initially switched to Dill due to a similar issue, which was solved by the switch).
However, if you are here with this issue and require Dill's features, then as Mike noted in the comments - it's possible to change the settings in dill.settings to have Dill behave pickle referenced items only with joblib mode, effectively mirroring pickle's standard pickling behaviour.

How to parallelize a class method or share class object between processes in python?

I have created a class containing all its instances and need to parallelize the process of instantiation, but cannot solve the problem of sharing the class as a class object. Is it possible in python 2.7 using multiprocessing?
OUTPUT_HEADINGS = []
class MyContainer(object):
"""
"""
instances = []
children = []
#classmethod
def export_to_csv(cls):
with open(args.output, "w") as output_file:
f_csv = csv.DictWriter(output_file, fieldnames=OUTPUT_HEADINGS)
f_csv.writeheader()
for instance in cls.instances:
f_csv.writerow(instance.to_dict())
def __new__(cls, dat_file):
try:
tree = ElementTree.parse(dat_file)
cls.children = tree.findall("parent_element/child_element")
except ElementTree.ParseError as err:
logging.exception(err)
if not cls.children:
msg = ("{}: No \"parent_element/child_element\""
" element found".format(os.path.basename(dat_file)))
logging.warning(msg)
cls.children = []
return False
else:
instance = super(MyContainer, cls).__new__(cls, dat_file)
instance.__init__(dat_file)
cls.instances.append(instance)
cls.children = []
return True
def __init__(self, dat_file):
self._name = os.path.basename(dat_file)
self.attr_value_sum = defaultdict(list)
var1 = MyContainer.children[0].find("var1")
var2 = MyContainer.children[0].get("var2")
cat_name = "{}.{}".format(var1, var2)
if cat_name not in OUTPUT_HEADINGS:
OUTPUT_HEADINGS.append(cat_name)
# processing and summarizing of xml data
def to_dict(self):
return output_dict
def main():
i = 0
try:
for f in FILE_LIST:
i += 1
print "{}/{}: {} in progress...".format(i, len(FILE_LIST), f)
print "{}".format("...DONE" if MyContainer(f) else "...SKIPPED")
except Exception as err:
logging.exception(err)
finally:
MyContainer.export_to_csv()
if __name__ == '__main__':
FILE_LIST = []
for d in args.dirs:
FILE_LIST.extend(get_name_defined_files(dir_path=d,
pattern=args.filename,
recursive=args.recursive))
main()
I tried to use multiprocessing.managers.BaseManager, to create a proxy for MyContainer class, but it can only create an instance object this way. I want actually to parallelize the MyContainer(dat_file) call.

Preset Variable with Pickle

import time
from random import randint
import pickle
MaTC = 1
MaTC = pickle.dump(MaTC, open("MaTCFile.txt", "wb"))
AnTC = 2
AnTC = pickle.dump(AnTC, open("AnTCFile.txt", "wb"))
AuTC = 3
AuTC = pickle.dump(AuTC, open("AuTCFile.txt", "wb"))
AlTC = 3
AlTC = pickle.dump(AlTC, open("AlTCFile.txt", "wb"))
JaTC = 3
JaTC = pickle.dump(JaTC, open("JaTCFile.txt", "wb"))
print("Hello Jacob")
time.sleep(1)
print("Choose an option!")
print("(1) Selecting Cleaners")
print("(2) Edit Cleaning Information")
print("(3) Reserved")
MenuChoice = input(": ")
if MenuChoice == "1":
print("Selecting Cleaner Function")
elif MenuChoice == "2":
print("(1) Check cleaning info")
print("(2) Enter New Cleaning info")
InfoSelect = input(": ")
if InfoSelect == "2":
Mare = { "Mares Times Cleaned": MaTC}
Andre = { "Andres Times Cleaned": AnTC}
Al = { "Als Times Cleaned": AlTC}
Austin = { "Austins Times Cleaned": AuTC}
Jacob = { "Jacobs Times Cleaned": JaTC}
pickle.dump( Mare, open ("MaresFile.txt", "wb"))
pickle.dump( Jacob, open ("JacobsFile.txt", "wb"))
pickle.dump( Andre, open ("AndresFile.txt", "wb"))
pickle.dump( Austin,open ("AustinsFile.txt", "wb"))
pickle.dump( Al, open ("AlsFile.txt", "wb"))
print(Mare)
print(Austin)
print(Jacob)
print(Andre)
print(Al)
Okay so basically what I am trying to achieve is have the MaTC, AnTC, AuTC, AlTC, and JaTC Variables be preset as 1, 2, 3, 3, and 3 at the first time running the program. But when I want to add 2 to say MaTC making it 3, When I start the program again I want it to = 3 on startup. I just started python a few days ago, I would love the feedback!
Use exceptions to handle the case where the file does not exist (i.e. first run of the program)
try:
f = open("MaTCFile.dat", "rb") # Pickle is not a .txt format
MaTC = pickle.load(f)
except IOError:
# Initialise default value
MaTC = 1
I would also be inclined to store all the variables in a single file using a dict:
default_data = {
"MaTC": 1, "AnTC": 2, # ... etc.
as this would make the program more manageable.
Here is a refactored version of your program. It uses the pickle module and demonstrates usage of pickletools, zlib, and a few other module. Hopefully, the code will help you further along in writing your program. One subject that you probably will want to research in the future is that of databases.
import os
import pickle
import pickletools
import sys
import zlib
SETTINGS_FILE = 'settings.sav'
def main():
"""Present the user with a cleaning application."""
settings = load_settings(SETTINGS_FILE)
print('Hello, Jacob!')
while True:
if show_menu(settings,
select_cleaners,
edit_cleaning_information,
reserved,
close_program):
break
settings.save(SETTINGS_FILE)
def load_settings(path):
"""Create the settings schema and load them from file is possible."""
settings = Namespace(mares=Parameter(1, is_positive_int),
andres=Parameter(2, is_positive_int),
austin=Parameter(3, is_positive_int),
al=Parameter(3, is_positive_int),
jacob=Parameter(3, is_positive_int))
if os.path.isfile(path):
settings.load(path)
return settings
def is_positive_int(value):
"""Ensure that the value is valid for the settings."""
return isinstance(value, int) and value >= 0
def show_menu(context, *args):
"""Help display a menu to the user."""
for option, function in enumerate(args, 1):
print(option, '-', function.__doc__)
while True:
number = get_int('Please enter an option: ') - 1
if 0 <= number < len(args):
return args[number](context)
else:
print('Your number was out of range.')
def get_int(prompt):
"""Get a valid number from the user."""
while True:
try:
text = input(prompt)
except EOFError:
sys.exit()
else:
try:
return int(text)
except ValueError:
print('You did not enter an integer.')
def select_cleaners(context):
"""Choose this to select your cleaner."""
print('Selecting Cleaning Function')
def edit_cleaning_information(context):
"""You can edit the cleaning information."""
show_menu(context, check_cleaning_info, enter_new_cleaning_info)
def reserved(context):
"""This is reserved for future use."""
print('NOT AVAILABLE')
def close_program(context):
"""Close the program."""
return True
def check_cleaning_info(context):
"""Show all cleaning info."""
print('Mares:', context.mares)
print('Andres:', context.andres)
print('Austin:', context.austin)
print('Al:', context.al)
print('Jacob:', context.jacob)
def enter_new_cleaning_info(context):
"""Enter in additional cleaning information."""
while True:
name = input('Who needs cleaning info adjusted? ').capitalize()
if name == 'Mares':
context.mares += get_int('Add to Mares: ')
elif name == 'Andres':
context.andres += get_int('Add to Andres: ')
elif name == 'Austin':
context.austin += get_int('Add to Austin: ')
elif name == 'Al':
context.al += get_int('Add to Al: ')
elif name == 'Jacob':
context.jacob += get_int('Add to Jacob: ')
else:
continue
break
###############################################################################
class _Settings:
"""_Settings(*args, **kwargs) -> _Settings instance"""
#staticmethod
def _save(path, obj):
"""Save an object to the specified path."""
data = zlib.compress(pickletools.optimize(pickle.dumps(obj)), 9)
with open(path, 'wb') as file:
file.write(data)
#staticmethod
def _load(path):
"""Load an object from the specified path."""
with open(path, 'rb') as file:
data = file.read()
return pickle.loads(zlib.decompress(data))
class Namespace(_Settings):
"""Namespace(**schema) -> Namespace instance"""
def __init__(self, **schema):
"""Initialize the Namespace instance with a schema definition."""
self.__original, self.__dynamic, self.__static, self.__owner = \
{}, {}, {}, None
for name, value in schema.items():
if isinstance(value, _Settings):
if isinstance(value, Namespace):
if value.__owner is not None:
raise ValueError(repr(name) + 'has an owner!')
value.__owner = self
self.__original[name] = value
else:
raise TypeError(repr(name) + ' has bad type!')
def __setattr__(self, name, value):
"""Set a named Parameter with a given value to be validated."""
if name in {'_Namespace__original',
'_Namespace__dynamic',
'_Namespace__static',
'_Namespace__owner',
'state'}:
super().__setattr__(name, value)
elif '.' in name:
head, tail = name.split('.', 1)
self[head][tail] = value
else:
attr = self.__original.get(name)
if not isinstance(attr, Parameter):
raise AttributeError(name)
attr.validate(value)
if value == attr.value:
self.__dynamic.pop(name, None)
else:
self.__dynamic[name] = value
def __getattr__(self, name):
"""Get a Namespace or Parameter value by its original name."""
if '.' in name:
head, tail = name.split('.', 1)
return self[head][tail]
if name in self.__dynamic:
return self.__dynamic[name]
attr = self.__original.get(name)
if isinstance(attr, Namespace):
return attr
if isinstance(attr, Parameter):
return attr.value
raise AttributeError(name)
__setitem__ = __setattr__
__getitem__ = __getattr__
def save(self, path):
"""Save the state of the entire Namespace tree structure."""
if isinstance(self.__owner, Namespace):
self.__owner.save(path)
else:
self._save(path, {Namespace: self.state})
def load(self, path):
"""Load the state of the entire Namespace tree structure."""
if isinstance(self.__owner, Namespace):
self.__owner.load(path)
else:
self.state = self._load(path)[Namespace]
def __get_state(self):
"""Get the state of this Namespace and any child Namespaces."""
state = {}
for name, types in self.__static.items():
box = state.setdefault(name, {})
for type_, value in types.items():
box[type_] = value.state if type_ is Namespace else value
for name, value in self.__original.items():
box = state.setdefault(name, {})
if name in self.__dynamic:
value = self.__dynamic[name]
elif isinstance(value, Parameter):
value = value.value
else:
box[Namespace] = value.state
continue
box.setdefault(Parameter, {})[type(value)] = value
return state
def __set_state(self, state):
"""Set the state of this Namespace and any child Namespaces."""
dispatch = {Namespace: self.__set_namespace,
Parameter: self.__set_parameter}
for name, box in state.items():
for type_, value in box.items():
dispatch[type_](name, value)
def __set_namespace(self, name, state):
"""Set the state of a child Namespace."""
attr = self.__original.get(name)
if not isinstance(attr, Namespace):
attr = self.__static.setdefault(name, {})[Namespace] = Namespace()
attr.state = state
def __set_parameter(self, name, state):
"""Set the state of a child Parameter."""
attr = self.__original.get(name)
for type_, value in state.items():
if isinstance(attr, Parameter):
try:
attr.validate(value)
except TypeError:
pass
else:
if value == attr.value:
self.__dynamic.pop(name, None)
else:
self.__dynamic[name] = value
continue
if not isinstance(value, type_):
raise TypeError(repr(name) + ' has bad type!')
self.__static.setdefault(name, {}).setdefault(
Parameter, {})[type_] = value
state = property(__get_state, __set_state, doc='Namespace state property.')
class Parameter(_Settings):
"""Parameter(value, validator=lambda value: True) -> Parameter instance"""
def __init__(self, value, validator=lambda value: True):
"""Initialize the Parameter instance with a value to validate."""
self.__value, self.__validator = value, validator
self.validate(value)
def validate(self, value):
"""Check that value has same type and passes validator."""
if not isinstance(value, type(self.value)):
raise TypeError('Value has a different type!')
if not self.__validator(value):
raise ValueError('Validator failed the value!')
#property
def value(self):
"""Parameter value property."""
return self.__value
###############################################################################
if __name__ == '__main__':
main()

Python generate sorted list

I want to compress my movies automatically. So I've written a mediainfo wrapper class in python, to generate a xml output, which I then parse to a movieinfo class, with a list of audio and subtitle tracks.
__author__ = 'dominik'
class Error(Exception):
""" Error class
"""
class ValidationError(Error):
""" Invalid or missing xml items
"""
class MovieInfo(object):
""" Description of movie file
"""
def __init__(self, media_info):
self._video_track = None
self._audio_tracks = []
self._subtitle_tracks = []
self.valid_movie = True
for track in media_info.tracks:
if track.track_type == "Audio":
self._audio_tracks.append(AudioTrack(track))
elif track.track_type == "Text":
self._subtitle_tracks.append(SubtitleTrack(track))
elif track.track_type == "Video":
self._video_track = VideoTrack(track)
#property
def audio_tracks(self):
if not hasattr(self, "_audio_tracks"):
self._audio_tracks = []
if len(self._audio_tracks) != 0:
return self._audio_tracks
#property
def subtitle_tracks(self):
if not hasattr(self, "_subtitle_tracks"):
self._subtitle_tracks = []
if len(self._subtitle_tracks) != 0:
return self._subtitle_tracks
class Track(object):
""" Abstract track class for audio and subtitle tracks
"""
__KNOWN_LANGUAGE_CODES = {"en": "ENG", "de": "DE"}
def __init__(self, track, valid_codecs):
self._valid = True
track_id = int(track.id)
codec_id = self._determine_codec(track.codec_id, valid_codecs)
language = self._determine_language(track.language)
self._id = track_id
self._codec_id = codec_id
self._language = language
def _determine_codec(self, track_codec, types):
result = types.get(track_codec, None)
if result is None:
self._valid = False
return result
def _determine_language(self, track_language, types=__KNOWN_LANGUAGE_CODES):
result = types.get(track_language, None)
if result is None:
self._valid = False
return result
class AudioTrack(Track):
""" Audio track class
"""
__KNOWN_AUDIO_CODECS = {"A_DTS": "DTS", "A_AC3": "AC3"}
def __init__(self, track):
self._type = 1
Track.__init__(self, track, self.__KNOWN_AUDIO_CODECS)
class SubtitleTrack(Track):
""" Subtitle track class
"""
__KNOWN_SUBTITLE_CODECS = {"S_VOBSUB": "VOBSUB"}
def __init__(self, track):
self._type = 2
if track.forced == "Yes":
self._forced = True
else:
self._forced = False
Track.__init__(self, track, self.__KNOWN_SUBTITLE_CODECS)
class VideoTrack(object):
""" Video track class (only one video track in movie info!)
"""
def __init__(self, track):
self._type = 0
self._framerate = float(track.frame_rate)
self._width = track.width
self._height = track.height
Here is the mediainfo class (it's the pymediainfo class):
from subprocess import Popen
import os
from tempfile import mkstemp
from bs4 import BeautifulSoup, NavigableString
from setuptools.compat import unicode
class Track(object):
""" Hold the track information
"""
def __getattr__(self, item):
try:
return object.__getattribute__(self, item)
except:
pass
return None
def __init__(self, xml_track):
self.xml_track = xml_track
self.track_type = xml_track.attrs["type"]
for child in self.xml_track.children:
if not isinstance(child, NavigableString):
node_name = child.name.lower().strip()
node_value = unicode(child.string)
node_other_name = "other_%s" % node_name
if getattr(self, node_name) is None:
setattr(self, node_name, node_value)
else:
if getattr(self, node_other_name) is None:
setattr(self, node_other_name, [node_value, ])
else:
getattr(self, node_other_name).append(node_value)
for key in [c for c in self.__dict__.keys() if c.startswith("other_")]:
try:
primary = key.replace("other_", "")
setattr(self, primary, int(getattr(self, primary)))
except:
for value in getattr(self, key):
try:
actual = getattr(self, primary)
setattr(self, primary, int(value))
getattr(self, key).append(actual)
break
except:
pass
def __repr__(self):
return("<Track id='{0}', type='{1}'>".format(self.id, self.track_type))
def to_data(self):
data = {}
for k, v in self.__dict__.items():
if k != 'xml_track':
data[k] = v
return data
class Mediainfo(object):
""" MediaInfo wrapper
"""
def __init__(self, xml):
self.xml_dom = xml
if isinstance(xml, str):
self.xml_dom = BeautifulSoup(xml, "xml")
def _populate_tracks(self):
if self.xml_dom is None:
return
for xml_track in self.xml_dom.Mediainfo.File.find_all("track"):
self._tracks.append(Track(xml_track))
#property
def tracks(self):
if not hasattr(self, "_tracks"):
self._tracks = []
if len(self._tracks) == 0:
self._populate_tracks()
return self._tracks
#staticmethod
def parse(filename):
filehandler_out, filename_out = mkstemp(".xml", "mediainfo-")
filehandler_err, filename_err = mkstemp(".error", "mediainfo-")
filepointer_out = os.fdopen(filehandler_out, "r+b")
filepointer_err = os.fdopen(filehandler_err, "r+b")
mediainfo_command = ["mediainfo", "-f", "--Output=XML", filename]
p = Popen(mediainfo_command, stdout=filepointer_out, stderr=filepointer_err)
p.wait()
filepointer_out.seek(0)
xml_dom = BeautifulSoup(filepointer_out.read(), "xml")
filepointer_out.close()
filepointer_err.close()
print(xml_dom)
return Mediainfo(xml_dom)
def to_data(self):
data = {'tracks': []}
for track in self.tracks:
data['tracks'].append(track.to_data())
return data
This class gives me every track in the xml and then I parse the relevant info in movieinfo.
Ok now I have a list of audiotracks e.g. 3 tracks one in german language and DTS, one in german and AC3 and one in english and AC3. Now I want to get the ids from the tracks in the format "1,2,3" to give it to handbrake cli.
My problem is the order of the tracks. If there is a german DTS track this schould be the first track, the second track should be also the first, but compressed to aac and the third track should be one english track in AAC. If there is only a german AC3 track then the first track should be this track but compressed to AAC, and the second track should englisch and AAC.
I don't know exactly how I can achive that, can you help me? I'm new to python, and come from C, C++ and C#. In C# this is very easy to get with lambda.
Assuming you know to define a compare-tor that given two items can define which is bigger then Python functions as well as C or C++.
Start here -
1. https://wiki.python.org/moin/HowTo/Sorting/
https://developers.google.com/edu/python/sorting
http://docs.python.org/2/library/functions.html#sorted
Using sorted method and define the key you want.

Categories