script to check sequence number in a binary file This is a script to check the sequence number (8 bit unsigned integer) in a binary file and make sure it is incrementing till the end and not missing any sequence number in between. Sequence number is some 'x' bytes from a sentinel found in the binary file. The fixed size packet (some audio data) in binary file gets repeated and this aforementioned packet also has the sentinel along with other audio data.

I am new to python so I'd appreciate a code review.

#!/usr/bin/python
import sys,getopt

def parse_arguments():
  def usage():
    print("python script to check missing sequence number in a binary file")
    print("pass filename, packet_size(optional) and sequence offset where sequence number is found in the packet")
    print("python %s -f filename -s packet_size -i sequence_offset"% (sys.argv[0]))

  filename, packet_size, sequence_offset = None, 0, None
  try:
      opts,args = getopt.getopt(sys.argv[1:],'ha:f:i:s:', ['help', 'filename', 'index'])
  except getopt.GetoptError as err:
      print(err)
      sys.exit(2)

  if len(opts) != 0:
     for (o, a) in opts:
        if o in ('-h', '--help'):
           usage()
           sys.exit()
        elif o in ('-f', '--filename'):
           filename = a
        elif o in ('-s', '--packet_size'):
           packet_size = int(a)
        elif o in ('-i', '--sequence_offset'):
           sequence_offset = int(a)
        else:
           usage()
           sys.exit(2)
  else:
     usage()
     sys.exit(2)
  return filename, packet_size, sequence_offset 


def is_sequence_missing(filename, packet_size = 5392, sequence_offset = 11):
  def get_sequence_idx(filename, sentinel = ['0x4f', '0x5f', '0x4d', '0x41']):
    blob = None
    with open(filename,"rb") as f:
      blob = f.read()
      len_sentinel = len(sentinel)
      for i in range(len(blob)):
        if i + len_sentinel &lt; len(blob) and sentinel == [hex(ord(j)) for j in blob[i:i+len_sentinel]]:
          return (blob, i)
    return (blob, None)

  missing, start, counter = False, True, 0

  blob, idx = get_sequence_idx(filename)
  if not idx:
    print("sentinel not found")
    return not missing
  print("sentinel starting at 0x%x"% (idx))

  for i in range(idx - sequence_offset, len(blob), packet_size):
    v = ord(blob[i])
    if start:
      print("started with sequence number 0x%x"% (v))
      start = False
      counter = v + 1
      continue
    if counter == v:
      counter += 1
      if counter == 256:
        counter = 0
    else:
      print("not matching expected: 0x%x receieved: 0x%x at 0x%x"% (counter, v, i))
      counter = v + 1
      missing = True
  return missing

if __name__ == "__main__":
  filename, packet_size, sequence_offset = parse_arguments()
  print("passed filename %s packet_size %d sequence_offset %d"% (filename, packet_size, sequence_offset))
  if is_sequence_missing(filename, packet_size, sequence_offset) == True:
    print("sequence is missing")
  else:
    print("sequence is not missing")
Style

Python comes with an official style guide (often just called PEP8) which is commonly accepted among Python programmers. Since you said you are new to Python, I wholeheartedly recommend to read and follow it. 

E.g. one of the recommendations of the style guide is to indent code by 4 spaces per indentation level, while you have a mix of 2 and four spaces. Even if you decide to stick with 2 spaces: be consistent!

Another very widely accepted convention is to have no whitespace around = if used in keyword-arguments to functions, e.g. instead of


def is_sequence_missing(filename, packet_size = 5392, sequence_offset = 11): 



use 

def is_sequence_missing(filename, packet_size=5392, sequence_offset=11):


Outdated Python

Your code uses some parts of the Python language which have become a bit "rusty", i.e. have been superseded in later versions of the language. Especially in the light of soon to come end-of-life of Python 2.7, I would highly recommend to have a look at Python 3 or at least use "more current" features of Python 2.7.

A few examples:

Command-line arguments

Your code uses getopt to parse command-line arguments. getopt has been superseded by optparse, which in turn has been marked as deprecated starting from Python 2.7 to be replaced by argparse (see also this SO post from 2010!) from there on. Apart from being easier to use, argparse will e.g. also generate and show usage instructions if the user fails to give the correct arguments.

Further reading: The official and reasonably comprehensive tutorial on argparse.

String formatting

String output is generated using the old % formatting. As of Python 2.6, the recommended way is to use str.format for string formatting. If you happen to have access to Python 3.6 and later, also so called f-strings are available to you which make the whole thing even more comfortable. An example from your code:

"not matching expected: 0x%x received: 0x%x at 0x%x"% (counter, v, i)
"not matching expected: 0x{:x} received: 0x{:x} at 0x{:x}".format(counter, v, i)  # Python 2.6+
f"not matching expected: 0x{counter:x} received: 0x{v:x} at 0x{i:x}"  # Python 3.6+


Further reading: This blog post has a nice comparison of all three variants and their possibilities.

General recommendations

Variable definition

Usually it's always a good idea to define your variables as close as possible to where they are needed, and also arguably define one variable per line. This helps to avoid unused or uninitialized variables, although the later part is not so critical here in Python as it would be e.g. in C or C++.

Documentation

Also not strictly specific to Python: Do yourself (and possible also others) a favor and document your code. Maybe in a few months time where you have done something different you will (have to) come back and wonder why the default value for sequence_offset is 11. Python has a feature called docstrings which are also described in the style guide (and in more detail in PEP 257) and are very commonly found in Python code. docstrings are essentially """triple quoted strings""" placed immediately after the function definition, trying to describe what to expect from the function. As an example:

def is_sequence_missing(filename, packet_size=5392, sequence_offset=11):
    """Check file for gaps in the sequence number

    read up binray data from filename and check for ascending
    sequence numbers starting sequence_offset (default: 11) bytes away
    from the sentinel value
    """
    # rest of the code here


Documentation written using docstrings will be picked up by Python's built-in help(...) as well as by most Python IDEs.



That's all for now. Maybe I can spare some time tomorrow to have a closer look at the actual algorithm. Edit: See the second answer for a more detailed feedback on the actual algorithm.