Implementing a table in a PySNMP agent - python

I'm implementing a table using PySNMP as an SNMP agent.
I've followed the (PySNMP source) architecture of using an auto-generated TRS-MIB.py file, and a manually written __TRS-MIB.py file which contains the implementation.
I have made a working (2 row) table, but the code is really ugly, so there must be a better way of doing it. (Also, both rows will always have the same values, which is a bug.)
__TRS-MIB.py:
# Imported just in case new ASN.1 types would be created
from pyasn1.type import constraint, namedval
import time
# Imports
( Integer,
ObjectIdentifier,
OctetString, ) = mibBuilder.importSymbols(
"ASN1",
"Integer",
"ObjectIdentifier",
"OctetString"
)
( Bits,
Integer32,
ModuleIdentity,
MibIdentifier,
MibScalar,
MibScalarInstance,
TimeTicks, ) = mibBuilder.importSymbols(
"SNMPv2-SMI",
"Bits",
"Integer32",
"ModuleIdentity",
"MibIdentifier",
"MibScalar",
"MibScalarInstance",
"TimeTicks"
)
class TrsDeliveryTime(Integer32):
def clone(self, **kwargs):
if 'value' not in kwargs:
kwargs['value'] = int(time.time())
return Integer32.clone(self, **kwargs)
class TrsMessagesPerHour(Integer32):
def clone(self, **kwargs):
if 'value' not in kwargs:
kwargs['value'] = -int(time.time())
return Integer32.clone(self, **kwargs)
class TrsGatewayIndex(Integer32):
def clone(self, **kwargs):
if 'value' not in kwargs:
kwargs['value'] = 0
return Integer32.clone(self, **kwargs)
class TrsGatewayName(OctetString):
def clone(self, **kwargs):
if 'value' not in kwargs:
kwargs['value'] = 'SMG0'
return OctetString.clone(self, **kwargs)
class TrsGatewayState(OctetString):
def clone(self, **kwargs):
if 'value' not in kwargs:
kwargs['value'] = 'UP' + str(int(time.time()))
return OctetString.clone(self, **kwargs)
# Objects
( trsDeliveryTime,
trsMessagesPerHour,
trsGatewayTable,
trsGatewayEntry,
trsGatewayIndex,
trsGatewayName,
trsGatewayState, ) = mibBuilder.importSymbols(
'TRS-MIB',
'trsDeliveryTime',
'trsMessagesPerHour',
'trsGatewayTable',
'trsGatewayEntry',
'trsGatewayIndex',
'trsGatewayName',
'trsGatewayState',
)
__trsDeliveryTime = MibScalarInstance(trsDeliveryTime.name, (0,), TrsDeliveryTime(0))
__trsMessagesPerHour = MibScalarInstance(trsMessagesPerHour.name, (0,), TrsMessagesPerHour(0))
__trsGatewayIndex0 = MibScalarInstance(trsGatewayIndex.name, (0,), TrsGatewayIndex(0))
__trsGatewayName0 = MibScalarInstance(trsGatewayName.name, (0,), TrsGatewayName(0))
__trsGatewayState0 = MibScalarInstance(trsGatewayState.name, (0,), TrsGatewayState(0))
__trsGatewayIndex1 = MibScalarInstance(trsGatewayIndex.name, (1,), TrsGatewayIndex(0))
__trsGatewayName1 = MibScalarInstance(trsGatewayName.name, (1,), TrsGatewayName(0))
__trsGatewayState1 = MibScalarInstance(trsGatewayState.name, (1,), TrsGatewayState(0))
# Augmentions
# Exports
# Objects
mibBuilder.exportSymbols("__TRS-MIB",
trsDeliveryTime=__trsDeliveryTime,
trsMessagesPerHour=__trsMessagesPerHour,
trsGatewayTable=trsGatewayTable,
trsGatewayIndex0=__trsGatewayIndex0,
trsGatewayName0=__trsGatewayName0,
trsGatewayState0=__trsGatewayState0,
trsGatewayIndex1=__trsGatewayIndex1,
trsGatewayName1=__trsGatewayName1,
trsGatewayState1=__trsGatewayState1,
)

Not pretending this is a beautiful code, here's some optimization to your initial version. It's not tested as I do not have your TRS-MIB file at hand.
The idea is to specialize MibScalarInstance class (to make it serving different columns) by overriding its readGet() method which returns a var-bind pair to be reported back to SNMP Manager.
You should be able to run this MIB with the stock CommandResponder, just make sure to load up your MIB on startup.
import time
# Import managed objects
( trsDeliveryTime,
trsMessagesPerHour,
trsGatewayIndex,
trsGatewayName,
trsGatewayState, ) = mibBuilder.importSymbols(
'TRS-MIB',
'trsDeliveryTime',
'trsMessagesPerHour',
'trsGatewayIndex',
'trsGatewayName',
'trsGatewayState',
)
# Columnar managed objects instances implementation
class TrsDeliveryTimeInstance(MibScalarInstance):
def readGet(self, name, val, *args):
if name[-1] == 0: # Row #0
return self.name, self.syntax(int(time.time()))
elif name[-1] == 1: # Row #1
return self.name, self.syntax(time.time()//2)
else:
MibScalarInstance.readGet(self, name, val, *args)
class TrsMessagesPerHourInstance(MibScalarInstance):
def readGet(self, name, val, *args):
if name[-1] == 0: # Row #0
return self.name, self.syntax(-int(time.time()))
elif name[-1] == 1: # Row #1
return self.name, self.syntax(-time.time()//2)
else:
MibScalarInstance.readGet(self, name, val, *args)
class TrsGatewayIndexInstance(MibScalarInstance):
def readGet(self, name, val, *args):
if name[-1] == 0: # Row #0
return self.name, self.syntax(0)
elif name[-1] == 1: # Row #1
return self.name, self.syntax(1)
else:
MibScalarInstance.readGet(self, name, val, *args)
class TrsGatewayNameInstance(MibScalarInstance):
def readGet(self, name, val, *args):
if name[-1] == 0: # Row #0
return self.name, self.syntax('SMG0')
elif name[-1] == 1: # Row #1
return self.name, self.syntax('SMG1')
else:
MibScalarInstance.readGet(self, name, val, *args)
class TrsGatewayStateInstance(MibScalarInstance):
def readGet(self, name, val, *args):
if name[-1] == 0: # Row #0
return self.name, self.syntax('UP' + str(time.time()))
elif name[-1] == 1: # Row #1
return self.name, self.syntax('DOWN' + str(time.time()))
else:
MibScalarInstance.readGet(self, name, val, *args)
# Instantiate and export managed objects instances
mibBuilder.exportSymbols(
"__TRS-MIB",
# Row #0
TrsDeliveryTimeInstance(trsDeliveryTime.getName(), 0, trsDeliveryTime.getSyntax()),
TrsMessagesPerHourInstance(trsMessagesPerHour.getName(), 0, trsMessagesPerHour.getSyntax()),
TrsGatewayIndexInstance(trsGatewayIndex.getName(), 0, trsGatewayIndex.getSyntax()),
TrsGatewayNameInstance(trsGatewayName.getName(), 0, trsGatewayName.getSyntax()),
TrsGatewayStateInstance(trsGatewayState.getName(), 0, trsGatewayState.getSyntax()),
# Row #1
TrsDeliveryTimeInstance(trsDeliveryTime.getName(), 1, trsDeliveryTime.getSyntax()),
TrsMessagesPerHourInstance(trsMessagesPerHour.getName(), 1, trsMessagesPerHour.getSyntax()),
TrsGatewayIndexInstance(trsGatewayIndex.getName(), 1, trsGatewayIndex.getSyntax()),
TrsGatewayNameInstance(trsGatewayName.getName(), 1, trsGatewayName.getSyntax()),
TrsGatewayStateInstance(trsGatewayState.getName(), 1, trsGatewayState.getSyntax())
)

Related

Python: Compound functions inside class

I am trying to replicate something similar to model queries in Django.
# database.py
class ModelFactory(object):
def __init__(self, table):
self.table = table
def fields(self, *args):
str_columns = ''
for count, arg in enumerate(args):
if count == 0:
str_columns += arg
else:
str_columns += ', %s' % arg
self.str_columns = str_columns
def wheres(self, **kwargs):
str_wheres = ''
for count, (key, value) in enumerate(kwargs.items()):
if count == 0:
str_wheres += 'WHERE %s = %s' % (key, value)
else:
str_wheres += ' AND %s = %s' % (key, value)
self.str_wheres = str_wheres
My idea is to use it as follows:
from database import ModelFactory
myInstance = ModelFactory('myTable')
myQuery = myInstance.fields('column1', 'column2').wheres(column1 = 5)
I am not sure if I need a new class or function inside ModelFactory class that would take the results from 'fields' and 'wheres' to compile a SQL string to query? Like the following line:
cur.execute('SELECT column1, column2 from myTable WHERE column1 = 5')
I am also not sure if calling class.function1.function2 is correct? Django has the 'objects' word, e.g. Instance.objects.filter().exclude()
I have tried to change the code base as follows:
# database.py
class ModelFactory(object):
def __init__(self, table):
self.table = table
def objects(self):
def fields(self, **kwargs):
return self.f(**kwargs)
def wheres(self, *args):
return self.w(*args)
def f(self, *args):
str_columns = ''
for count, arg in enumerate(args):
if count == 0:
str_columns += arg
else:
str_columns += ', %s' % arg
self.str_columns = str_columns
def w(self, **kwargs):
str_wheres = ''
for count, (key, value) in enumerate(kwargs.items()):
if count == 0:
str_wheres += 'WHERE %s = %s' % (key, value)
else:
str_wheres += ' AND %s = %s' % (key, value)
self.str_wheres = str_wheres
But when I try the following:
from database import ModelFactory
myInstance = ModelFactory('myTable')
myQuery = myInstance.objects.fields('column1', 'column2').wheres(column1 = 5)
I get an AttributeError: 'function' object has no attribute 'fields'
If you want to chain object's method calls, you need to return that object from method. i.e. add return self to your methods.
So your class declaration probably should be something like the following:
class ModelFactory(object):
def __init__(self, table):
self.table = table
def fields(self, *args):
self.str_columns = ', '.join(args)
return self
def wheres(self, **kwargs):
str_wheres = ' AND '.join('{} = {}'.format(k, v) for k, v in kwargs.items())
self.str_wheres = 'WHERE {}'.format(str_wheres)
return self
def execute(self):
// ATTN! This code is prone to SQL injection. Do not use!
cur.execute('SELECT {columns} FROM {table} {wheres}'.format(columns=self.str_columns, table=self.table, wheres=self.wheres))
The problem is that you still have objects as a function:
def objects(self):
Currently, this means that you'll need to call it as a function - your myQuery line would need to look something like:
myQuery = myInstance.objects().fields(...
However, that still wouldn't be enough because fields and wheres are both only scoped within the objects function.
If you want to push down this route, then you will need to create a class that you instantiate inside your model under the attribute objects - Django uses QuerySet for this. If you check out the source you'll see how much magic is required to make something "simple" like objects work.
So a simple alternative?
You will have to make a new class similar to QuerySet which can provide the fields and wheres chainable functions - let's call it WernerfeuerSet:
class WernerfeuerSet:
def __init__(self, table):
self.table = table
def fields(self, **kwargs):
pass
def wheres(self, **kwargs):
pass
And now instantiate that within your ModelFactory - something like:
class ModelFactory:
def __init__(self, table):
self.objects = WernerfeuerSet(table)
Now your original query should be possible because objects is an attribute of the ModelFactory, rather than a function:
myQuery = myInstance.objects.fields('column1', 'column2').wheres(column1 = 5)

NameError: name '_length' is not defined

I'm using python3 and when trying to run the following code, I'm facing the error:
NameError: name '_length' is not defined
The code itself:
class OFPHELLO(GenericStruct):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._length = self.get_size()
_msg_type = OFPType.OFPT_HELLO
_build_order = ('header', 'x')
header = OFPHeader(type = _msg_type, length = _length)
x = UBInt8()
The problem is the _length variable that I'm passing in OFPHeader, the value of which is computed in GenericStruct. How can I compute the _length variable inside the OFPHELLO class and use it as parameter in the OFPHeader class?
Following the GenericStruct code:
class GenericStruct(object):
def __init__(self, **kwargs):
for a in kwargs:
try:
field = getattr(self, a)
field.value = kwargs[a]
except AttributeError:
raise OFPException("Attribute error: %s" % a)
def build(self):
hexa = ""
for field in self._build_order:
hexa += getattr(self, field).build()
return hexa
def parse(self, buff):
begin = 0
for field in self._build_order:
size = getattr(self, field).get_size()
getattr(self,field).parse(buff, offset=begin)
begin += size
def get_size(self):
tot = 0
for field in self._build_order:
tot += getattr(self, field).get_size()
return tot
- how have you defined (GenericStruct)
- header = OFPHeader(type = _msg_type, length = _lenght)
- correct the spelling to _length
-- and please post the entire code next time

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.

django custom field saves but not visualize

I created a Field to save the weekdays, used in a model like that (models.py):
from myfields import *
from django.db import models
class MyModel(models.Model):
weekdays = WeekdayField()
The file in which i have stored the form declaration is myfields.py and is this one:
from django.db import models
from django import forms
class Weekdays(object):
CHOICES = (
(1, "Monday"),
(2, "Tuesday"),
(4, "Wednesday"),
(8, "Thursday"),
(16, "Friday"),
(32, "Saturday"),
(64, "Sunday"),
)
MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY = [2**i for i in range(0,7)]
del i
def __init__(self, value=0):
self.raw = value
def __add__(self, other):
return Weekdays(self.raw | other)
def __sub__(self, other):
return Weekdays(self.raw & (-1 ^ other))
def __unicode__(self):
ret = []
for ele in Weekdays.CHOICES:
if (self.raw & ele[0]) >> ele[0] == 1:
ret.append(ele[1])
return ",".join(ret)
def __iter__(self):
ret = []
for ele in Weekdays.CHOICES:
if (self.raw & ele[0]) >> ele[0] == 1:
ret.append(ele)
return iter(ret)
def __len__(self):
i = 0
for n in range(7):
if (self.raw & n) >> n == 1:
i += 1
return i
class WeekdayField(models.Field):
description = "Multiple select of weekdays"
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 3
super(WeekdayField, self).__init__(*args, **kwargs)
def to_python(self, value):
if isinstance(value, int):
return [ wd for wd in Weekdays(value) ]
if isinstance(value, Weekdays):
return value
if isinstance(value, list): #list of weekstay
wd = Weekdays()
for val in value:
wd += int(val)
return wd
# PY to DB
def get_prep_value(self, value):
return value.raw
def formfield(self, **kwargs):
defaults = {'form_class': WeekdayFormField}
defaults.update(kwargs)
return super(WeekdayField, self).formfield(**defaults)
def get_internal_type(self):
return "IntegerField"
class WeekdayFormField(forms.MultipleChoiceField):
def __init__(self, *args, **kwargs):
if 'choices' not in kwargs:
kwargs['choices'] = Weekdays.CHOICES
kwargs.pop('max_length', None)
if 'widget' not in kwargs:
kwargs['widget'] = forms.widgets.CheckboxSelectMultiple
super(WeekdayFormField, self).__init__(*args, **kwargs)
def clean(self, value):
super(WeekdayFormField, self).clean(value)
possible_values = [ str(x[0]) for x in Weekdays.CHOICES ]
for v in value:
if not v in possible_values:
raise forms.ValidationError("Day not valid")
return value
Using this i can create and remove the elements through Django Admin, but when i modify a value i don't see the value that i stored before. I looked into the database and the value are stored right.
Any ideas on how to make me see what is the current value on Django Admin, during the modification?
I've the solution! it was a wrong the implementation of the methods Weekdays.__unicode__() and Weekdays.__iter__().
Finally i had also changed the two methods reported below, in WeekdayField.
#Class-type
class Weekdays(object):
...
def __unicode__(self): # Request for the matching
ret = []
for i,ele in enumerate(Weekdays.CHOICES):
if (self.raw & ele[0]) >> i == 1:
ret.append(unicode(ele[0]))
return ','.join(ret)
def __iter__(self):
for i,ele in enumerate(Weekdays.CHOICES):
if (self.raw & ele[0]) >> i == 1:
yield Weekdays(ele[0])
#Field to include in the model
class WeekdayField(models.Field):
...
# DB to PY
def to_python(self, value):
if isinstance(value, int):
return [ wd for wd in Weekdays(value) ]
if isinstance(value, unicode):
return [ wd for wd in Weekdays(int(value)) ]
if isinstance(value, Weekdays):
return value
if isinstance(value, list): #list of Weekdays
wd = []
for val in value:
wd += self.to_python(val)
return wd
#PY to DB
def get_prep_value(self, value):
if isinstance(value, Weekdays):
return value.raw
if isinstance(value, list):
sum_raw = 0
for val in value:
sum_raw += self.get_prep_value(val)
return sum_raw
I hope this will be helpful to someone!

Categories