Programming

Installing PyAudio on macOS

Installing pyaudio on macOS is almost straightforward, except that you need portaudio installed. The easiest way to remedy this is to use homebrew. If you have your homebrew installing in the recomended location it is as simple as the following:

brew install portaudio
pipenv install pyaudio

However if you have homebrew using a non-standard location, it requires a few additional settings, if you don’t have them set already.

brew install portaudio
export C_INCLUDE_PATH=~/homebrew/include/:$C_INCLUDE_PATH
export LIBRARY_PATH=~/homebrew/lib/:$LIBRARY_PATH
pipenv install pyaudio
Posted by Chad Dotson in Programming, 0 comments

Working with bytes in Python 3

Background

Sometimes you find yourself needing to work at the byte-level in an application you are working on. I feel that in Python there are not enough examples of how to do this. There is also a lot of potential to over-complicate the solution.

This Example

I plan to cover several aspects of working with bytes in this example. I’ll cover working with the struct package, the bytearray built-in and the ctypes module.

The Code

from ctypes import c_int, Structure
from struct import pack_into, unpack_from
  

# create 2 buffers, one smaller than the other for 
# demonstration purposes.
buff1 = bytearray(64)
buff2 = bytearray(32)

# first we'll use the struct package to initalize the arrays.

# initialize buff1 with 32 integers.
pack_into('I' * 16, buff1, 0, *range(0, 16))
print('Buffer 1:', unpack_from('I' * 16, buff1, 0))


# for the sake of demonstration, we'll work with buff2.
# copy part of buff1 into buff2, since we're using
# bytearrays, this should be equivalent to a memcpy
buff2[:] = buff1[:32]

# test it out, did we copy 32 bytes from buff1 into buff2?
print('Buffer 2:', unpack_from('I' * 8, buff2, 0), end='\n\n')

# We can also use the ctypes package to access the buffers

# We can access it piece-meal like this.  Note that this
# copies the buffer, if we didn't want a copy. from_buffer
# is the function we would use.
x = c_int.from_buffer_copy(buff2, 8)
y = c_int.from_buffer_copy(buff2, 12)
print(f'x, y as 2 standalone c_ints: {x.value}, {y.value}', end='\n\n')

# You can also create C Structures to access the data

# Define a simple ctypes structure.
class Point(Structure):
    _fields_ = [
        ('x', c_int),
        ('y', c_int)
    ]

p1 = Point.from_buffer_copy(buff2, 8)
print(f'x, y as elements of a ctype structure (copied): {p1.x}, {p1.y}')

# note that since this is a copy any manipulation doesn't
# effect the buffer.

p1.x, p1.y = 50, 51
print(f'p1.x, p1.y set to: {p1.x}, {p1.y}')
print('Show buff2 is unchanged:', unpack_from('I' * 8, buff2, 0), end='\n\n')

# so, if we wanted to directly manipulate the buffer using the structure
p2 = Point.from_buffer(buff2, 8)
print(f'x, y as elements of a ctype structure (not copied): {p1.x}, {p1.y}')

p2.x, p2.y = 100, 101
print(f'p2.x, p2.y set to: {p2.x}, {p2.y}')

# see that the 3rd and 4th element now been changed.
print('Show buff2 is changed:', unpack_from('I' * 8, buff2, 0), end='\n\n')

# finally note that the original buffer is unchanged.
print('Show buff1 unchanged:', unpack_from('I' * 16, buff1, 0))

Output

Buffer 1: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
Buffer 2: (0, 1, 2, 3, 4, 5, 6, 7)

x, y as 2 standalone c_ints: 2, 3

x, y as elements of a ctype structure (copied): 2, 3
p1.x, p1.y set to: 50, 51
Show buff2 is unchanged: (0, 1, 2, 3, 4, 5, 6, 7)

x, y as elements of a ctype structure (not copied): 50, 51
p2.x, p2.y set to: 100, 101
Show buff2 is changed: (0, 1, 100, 101, 4, 5, 6, 7)

Show buff1 unchanged: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)

A statement on copying

Be careful with slicing a python bytearray, bytes or array.array. Slicing creates a copy and can impact the performance of your application. There is a better way; enter memoryview. Memoryview works with anything that implements the Python Buffer Protocol and makes slicing very efficient. Slicing a memoryview will result in another memoryview, not a copy of the bytes represented.

Extra Reading

Posted by Chad Dotson in Programming, Software Engineering, 0 comments

Python CSV Module Oddity

A Python Oddity

I was using Python to encode a CSV file using a custom dialect recently when I noticed something odd. I noticed that the csv writer class takes an optional argument that enables you to change the line terminator.  That’s ok, however, the csv reader class does not honor the argument. So, through the default api, you can create CSV that you cannot read back in with the default api. According to the documentation, this applies to Python 2.7 through 3.7.  It also probably applies to versions < 2.7 but that documentation is no longer online.

Python.org Documentation

A Simple Script

I wrote the following simple script to illustrate this oddity.  Basically it has a list of lists that it converts to CSV and back using various line terminators, if the output differs from the source, the difference is displayed.

import csv
import io
import re


def convert_to_csv_and_back(rows: list, lineterminator: str):
    with io.StringIO() as o:
        writer = csv.writer(o, lineterminator=lineterminator)
        for row in rows:
            writer.writerow(row)

        with io.StringIO(o.getvalue()) as i:
            reader = csv.reader(i, lineterminator=lineterminator)
       
            return list(reader)

source = [
    ['this', 'is', 'row', '1'],
    ['this', 'is', 'row', '2'],
    ['this', 'is', 'row', '3']
]

lineterminators = ['|', ':', '\t', '\r\n']

for terminator in lineterminators:
    output = convert_to_csv_and_back(source, terminator)
    source_set = set(map(tuple, source))
    output_set = set(map(tuple, output))

    difference = source_set.symmetric_difference(output_set)
    to_from_csv_failed = len(difference)
    
    print(f'Line Terminator: {repr(terminator)}')
    print(f'To/From CSV Worked: {"No" if to_from_csv_failed else "Yes"}')
    print(f'Source: {source}')
    print(f'Output: {output}')
    
    if to_from_csv_failed:
        print(f'Difference: {difference}')

    print('--------------------------')

Results

As you can see from the results here; with the exception of ‘\r\n’, all the various line terminators  failed.

Line Terminator: '|'
To/From CSV Worked: No
Source: [['this', 'is', 'row', '1'], ['this', 'is', 'row', '2'], ['this', 'is', 'row', '3']]
Output: [['this', 'is', 'row', '1|this', 'is', 'row', '2|this', 'is', 'row', '3|']]
Difference: {('this', 'is', 'row', '1|this', 'is', 'row', '2|this', 'is', 'row', '3|'), ('this', 'is', 'row', '1'), ('this', 'is', 'row', '2'), ('this', 'is', 'row', '3')}
--------------------------
Line Terminator: ':'
To/From CSV Worked: No
Source: [['this', 'is', 'row', '1'], ['this', 'is', 'row', '2'], ['this', 'is', 'row', '3']]
Output: [['this', 'is', 'row', '1:this', 'is', 'row', '2:this', 'is', 'row', '3:']]
Difference: {('this', 'is', 'row', '1'), ('this', 'is', 'row', '2'), ('this', 'is', 'row', '1:this', 'is', 'row', '2:this', 'is', 'row', '3:'), ('this', 'is', 'row', '3')}
--------------------------
Line Terminator: '\t'
To/From CSV Worked: No
Source: [['this', 'is', 'row', '1'], ['this', 'is', 'row', '2'], ['this', 'is', 'row', '3']]
Output: [['this', 'is', 'row', '1\tthis', 'is', 'row', '2\tthis', 'is', 'row', '3\t']]
Difference: {('this', 'is', 'row', '1'), ('this', 'is', 'row', '1\tthis', 'is', 'row', '2\tthis', 'is', 'row', '3\t'), ('this', 'is', 'row', '3'), ('this', 'is', 'row', '2')}
--------------------------
Line Terminator: '\r\n'
To/From CSV Worked: Yes
Source: [['this', 'is', 'row', '1'], ['this', 'is', 'row', '2'], ['this', 'is', 'row', '3']]
Output: [['this', 'is', 'row', '1'], ['this', 'is', 'row', '2'], ['this', 'is', 'row', '3']]
--------------------------

Closing Thoughts

So, why would you want to specify the line terminators with the CSV module? There are probably only a handful of reasons, my only reason would be a custom dialect where I wanted to ensure that there were no carriage returns or line feeds in.  99.9% of the time, you want ‘\r\n’.

Posted by Chad Dotson in Programming, Software Engineering, 0 comments

Thoughts On Team Communication

Awhile back I had some thoughts on communication. If you’ve ever played World of Tanks Blitz you’d know that basically its a team of tanks against another team of tanks. With the pick up, fast paced nature communication is minimal at best (sometimes limited to a single “<<<<<<<<<” or “>>>>>>>>>” indicating which direction to take the offense). I found that teams that could coordinate with minimal communication, play their tank roles (scouts, mediums, heavies, and destroyers), and move fast could achieve massive overwhelming victories. Something similar is probably true in an agile/teamwork environment. Know your stuff, know your role, take opportunities, work together, succeed.

Posted by Chad Dotson in Doing Things Better, Software Engineering, 0 comments

Odd Behavior in Python 2.7

I was tinkering around with replacing the print statement with the print function in a Python 2 script when I ran across this peculiar oddity.

>>> from __future__ import print_function as print
  File "<stdin>", line 1
    from __future__ import print_function as print
                                                 ^
SyntaxError: invalid syntax
>>> from __future__ import print_function as print_
>>> from __future__ import print_function as print

Notice that sequence is just importing the future print function as different names in each iteration.  The oddity is that the first import fails but the third (which is exactly the same) succeeds after performing the second.

** Note: I don’t know if replacing print with the print from the futures module is a wise thing to do.  I was simply using it while trying out some code. **

Versions tested: Python 2.7.10 (OSX), Python 2.7.6 (Ubuntu 14.04).

Posted by Chad Dotson in Programming, 0 comments

You’re Too Close

Have you encountered the following scenario?

You are trying to solve a problem (or helping solve a problem) and know or at least think you know the solution.  You are in the middle of implementing it when someone else looks at it and says, “why don’t you do it this way, isn’t this way easier/better?”  Taking a step back, you realize that the question not only has merit but is a better and much more obvious solution; you can’t believe you missed it.

What happened?

I think its because you were too close to the problem and had developed a very narrow focus.  That narrow focus prevented you from seeing the better solution.  Perhaps this is even a variation of functional fixedness in that we’ve latched onto an idea of how to solve a problem and our mind’s may not see alternatives easily.

What can we do?

  • Think about the broad (or product) level goals regularly.
  • Entertain questions and/or suggestions from others.
  • Ask: “Is this the best way?”
  • Ask: “Is this the practical way?”
  • Don’t overthink the problem.
  • Get it working then evaluate the solution and/or do a code review!
Posted by Chad Dotson in Doing Things Better, Programming, Tips, 0 comments

Python Logging – Best Practices

The python logging module offers a wide variety of logging options and handlers.  One thing missing from the documentation is when to use each level.

A quick foreword

You really should familiarize yourself with the logging package.  How to create new loggers (I find creating them by module very useful).  There are many ways to configure logging, I tend to like dictConfig from logging.config (but start off with basicConfig form logging).

A Word on Optimal Setups

I prefer to setup my logging with each module having its own logger.  This allows me to configure logging levels at a package and/or module level.  I typically do the following in each module to create a logger.

from logging import getLogger
...
logger = getLogger(__name__)
...

Assuming my package structure consists of the following:

– foo (package)
—– core (module)
—– bar (module)

We can configure varying levels of logging for each element, as seen in the following snippet from a dictConfig.

...
'loggers': { 
    '': { 
        'handlers': ['default'],
        'level': 'INFO',
        'propagate': True
    },
    'foo': { 
        'handlers': ['default'],
        'level': 'WARNING',
        'propagate': True
    },
    'foo.bar': { 
        'handlers': ['default'],
        'level': 'DEBUG',
        'propagate': True
    },
}
...

In this example, the root ( ” ) logger (those not configured by any other settings) reports INFO level and up messages.  With the exception of the bar module, the foo package only reports WARNING level and up messages.  The bar module is set to a more verbose DEBUG level, to show information needed for debugging.

Selecting A Log Message Level

Out of the box, there are six default logging levels recognized by the logging module, most are self-explanatory.  I’ll just make some notes about usage.  (From here on out, I’ll refer to my logging instance as logger.)

For general status messages, you should use logger.info (INFO).  For errors, use either logger.critical (CRITICAL) or logger.error (ERROR).  For all exceptions, use logger.exception (ERROR).  logger.exception will automatically include stack trace information about the exception for you in the log. When you want verbose debugging information, use logging.debug (DEBUG)

In Closing

  • Use the logging module instead of print statements.
  • Always use logger.exception for logging exceptions.
  • Favor logger.debug for verbose log statements.
  • Favor logger.info for most other log statements (with the exception of errors).
  • Don’t forget that each of the logging functions uses C-style formatting.
Posted by Chad Dotson in Doing Things Better, Key Concepts, Programming, Software Engineering, Technology, 0 comments

Installing Technical Analysis Library for Python

I’m tinkering with some financial analysis scripts so when I got to looking into some useful python packages, Technical Anaysis Library popped up.  The python bindings require the TA Lib (Technical Analysis Library) which on osx is available via homebrew.  Now, when I originally installed I didn’t want to install it globally so I’ve got the less preferred, local install setup.  This local install results in the following necessary commands to get the pip package to install correctly.

# Activate homebrew
export PATH=~/homebrew/bin/:$PATH

# install TA-lib
brew install TA-lib

# set C_INCLUDE_PATH
export C_INCLUDE_PATH=~/homebrew/include/:$C_INCLUDE_PATH

# set LIBRARY_PATH
export LIBRARY_PATH=~/homebrew/lib/:$LIBRARY_PATH

Now that I’ve brew installed TA Lib and set the new include and library path, I can install the python bindings via pip.

# make virtualenv using virtualenv wrapper.
mkvirtualenv financial

pip install numpy
pip install TA-Lib

 

Posted by Chad Dotson in Programming, 2 comments