2011/06/26

Работа с COM портом в python: PyVISA или pySerial?

Для питона существует два модуля для работы с последовательными портами, я бы однозначно рекомендовал pySerial по нескольким причинам:

1. PyVISA сложен в установке, нужно дополнительно устанавливать библиотеку nivisa.
2. PyVISA не видит эмулированный через USB последовательный порт (т.е. переходник USB->COM).
3. В PyVISA нельзя задавать порт напрямую через файл устройства, например, в случае переходника USB->COM это будет /dev/ttyUSB0.

PySerial всех этих недостатков лишен, при этом очень прост в использовании. При работе с последовательным портом не надо забывать выставить одинаковые настойки на компе и собственно устройстве, в особенности это касается скорости передачи и бита четности. Все настройки могут задаваться при создании экземпляра объекта Serial или после через атрибуты.

Страница документации.

2011/05/29

Парсим логи при помощи генераторов - 2

Дано: сотни логов веб-сервера, разбросанные по разным директориям. Возможно, заархивированные.
Требуется: понять сколько байтов было передано :)

В питоне есть замечательная функция os.walk(), позволяющая блуждать по файловой системе:

import os

for path, dirlist, filelist in os.walk(topdir):
    # path : текущая директория
    # dirlist : список поддиректорий
    # filelist : список файлов
    ...

Для достижения цели потребуется создать несколько функций:
1. Функция-генератор, возвращающая список файлов по заданному шаблону:

import os
import fnmatch
 
def gen_find(filepat,top):
    for path, dirlist, filelist in os.walk(top):
        for name in fnmatch.filter(filelist, filepat):
            yield os.path.join(path, name)

Примеры использования:

pyfiles = gen_find('*.py', '/')
logs = gen_find('access-log*', '/usr/www/')

2. Функция-генератор, которая принимает список файлов, и, если среди них есть архивы, то возвращает распакованные файлы. Если файл не архив, то просто открывает его:

import gzip
import bz2
 
def gen_open(filenames):
    for name in filenames:
        if name.endswith(".gz"):
            yield gzip.open(name)
        elif name.endswith(".bz2"):
            yield bz2.BZ2File(name)
        else:
            yield open(name)

3. Функция (разумеется генератор), которая возвращает единую последовательность результатов, принимая при этом несколько последовательностей (в нашем случае передаем открытые файлы, а получаем последовательность строк):

def gen_cat(sources):
    for s in sources:
        for item in s:
            yield item

Ну вот, после реализации всех этих функций осталось только немного подправить прошлый исходник, чтобы он теперь работал с любым количеством файлов:

filenames = gen_find('access-log*', '/usr/www')
logfiles = gen_open(filenames)
loglines = gen_cat(logfiles)
bytecolumn = (line.rsplit(None, 1)[1] for line in loglines)
bytes = (int(x) for x in bytecolumn if x != '-')
print "Total", sum(bytes)

Особые эстеты могут свернуть этот исходник вплоть до 1 строчки, благодаря тому факту, что в питоне все функции - высшего порядка.

Парсим логи при помощи генераторов

Задача: понять по логам Apache сколько байтов мы передали.
NB: файлы мб большие (несколько гигов).

Формат логов примерно следуюший:

217.168.25.4 - - [28/May/2011:14:06:27 +0400] "GET / HTTP/1.0 200 8509

Надо получать из каждой строки последнюю циферку, при этом если ничего не передавалось в запросе (например, произошла ошибка), то вместо циферки будет дефис. Как и в прошлый раз предлагаю два варианта: быдлокодерский и православный. Итак, номер один, без использования генератора:

wwwlog = open("access-log")
total = 0
for line in wwwlog:
    bytestr = line.rsplit(None,1)[1]
    if bytestr != '-':
        total += int(bytestr)
print "Total", total

И, с генератором:

wwwlog = open("access-log")
bytecolumn = (line.rsplit(None,1)[1] for line in wwwlog)
bytes = (int(x) for x in bytecolumn if x != '-')
print "Total", sum(bytes)

Выглядит компактнее, не правда ли? В данном случае используется выражение-генератор, эдакий "цикл for наоборот", который выдает кортеж из результатов, суммирующийся далее.

2011/05/18

Скрипт для скачивания содержимого по ссылкам

Тестовое задание в одной конторе (не будем называть пальцем):

Напишите скрипт, который будет считывать список URL из файла (одна строка - один URL), скачивать их не более чем в N потоков и сохранять каждую страницу в отдельный файл. N задается аргументом командной строки и по умолчанию равно 10. Имена результирующих файлов значения
не имеют.

import sys
import threading
import Queue
import urllib2

class DownloadThread(threading.Thread):
    def run(self):
        headers = {'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'}
        while urlsPool.qsize() > 0:
            logfile = open(str(urlsPool.qsize()), 'w')
            req = urllib2.Request(urlsPool.get(), None, headers)
            logfile.write(urllib2.urlopen(req).read())
            logfile.close()

if len(sys.argv) < 2:
    print 'Usage: downloader.py [-n <number>] FILE\n\
"-n <number>" - number of threads (default 10)'
    sys.exit(1)
if len(sys.argv) == 4 and sys.argv[1] == '-n':
    threads = int(sys.argv[2])
else:
    threads = 10
urlsPool = Queue.Queue(0)
for url in open(sys.argv[-1]):
    urlsPool.put(url)
for x in xrange(threads):
    DownloadThread().start()

Немного про итераторы в питоне

В рамках акции по избавлению своих программ на питоне от быдлокода, я, как бывший программист на Си, открыл для себя много занимательных вещей. Сегодня немного про итераторы - объекты, которые позволяют программисту перебирать значения. Технически это реализуется с помощью метода next, который при каждом вызове возвращает следующий элемент коллекции. Например, так прочитает содержимое файла бывший быдлопрогаммист на Си:

file = open('file.txt')
while True:
    line = file.readline()
    if not line: break
    print line

А так это сделает грамотный питонер (объект файла поддерживает итерационный протокол, который используется в цикле for):

for line in open('file.txt')
    print line

Здесь циклом for неявно вызывается метод next и в конце обрабатывается исключение StopIteration, открытие файла также происходит неявно, поэтому можно избежать проблем с памятью при работе с большими файлами.

У словарей тоже есть итератор! Так можно выполнить обход всех ключей словаря:

D = {'a' : 1, 'b' : 2, 'c' : 3}
for key in D:
    print key, D[key]

Итераторы в строках тоже могут быть весьма полезны. Следующий код получает и выводит на экран каждый 2 символ строки S (ещё здесь используется срез с третьим параметром, про который многие забывают):

for x in S[::2]:
    print x

Соотвествующий быдлокод, который к тому же работает медленнее, т.к. при каждом проходе цикла осуществляется доступ к объекту строки:

for i in range(0, len(S), 2):
    print S[i]

В целом в питоне любая коллекция поддерживает итерационный протокол, и его использование в коде не только способствует его компактности, но и благотворно отражается на скорости программы.

2011/03/24

Бразильский танец с бубном

Очередная моя статья в свежей прессе!

На этот раз рассуждал о настройке, оптимизации работы и обеспечении безопасности Samba-клиента в Linux.

2011/03/22

Убираем анимацию табов в Firefox 4

Сегодня (почтенная публика уже, наверное, в курсе) состоялся релиз Firefox 4! Всё вроде хорошо: и быстрота, и простота, и так далее. Но, следуя трендам, разработчики прикрутили анимацию при открытии таба, которая лично меня ужасно раздражает.
А убрать её через штатное меню настройки нельзя, а можно вот так: надо набрать в строке адреса "about:config", в фильтре ввести "tab", найти параметр browser.tabs.animate и установить его значение в false.

2011/03/21

Выставляем правильное разрешение на внешнем дисплее

Как выяснилось, мой уютный ThinkPad с Арчем на борту не сильно дружит с телеком Samsung по VGA кабелю, а именно дает выставлять разрешение максимум 1024x768, хотя поддерживается там 720p (1360x768). Причину такого поведения доподлинно выяснить не удалось, но тут скорее всего два варианта: либо дрова неправильно обрабатывают EDID блок, либо этот блок неправильно формируется на телеке (либо я вообще неправильно настроил иксы).
Для исправления ситуации командуем, чтобы узнать параметры этого разрешения, так называемый VESA CVT mode line:
$ cvt 1360 768
Далее добавляем этот режим в список доступных:
$ xrandr --newmode "1360x768_60.00"   84.75  1360 1432 1568 1776  768 771 781 798 -hsync +vsync
И добавляем режим для конкретного выхода:
$ xrandr --addmode VGA1 1360x768_60.00
После этого нужное разрешение будет доступно для выбора в xrandr или фронтэнде к нему для вашего WM/DE. Я, вследствие природной лени, пользуюсь lxrandr. Чтобы каждый раз не вбивать команды, можно записать их в файлы ~/.xinitrc или ~/.xprofile - они выполнятся при старте иксов.

Либо можно добавить в xorg.conf блок типа такого:
Section "Monitor"
    Identifier "External VGA"
    Modeline "1360x768_60.00"   84.75  1360 1432 1568 1776  768 771 781 798 -hsync +vsync
    Option "PreferredMode" "1360x768_60.00"
EndSection

2011/03/11

Считаем количество ненулевых битов

"Предложите способ посчитать количество ненулевых битов в содержимом файла с именем blob.dat". Предлагаю:

#!/usr/bin/python2.7

def bstr(n):
    return ''.join([str(n >> x & 1) for x in (7,6,5,4,3,2,1,0)])

f = file('blob.dat', 'rb')
bytes = f.read()
sheet = ''.join([bstr(ord(c)) for c in bytes])

amount = 0
for bit in sheet:
    if bit == '1': amount += 1

print amount

2011/03/03

Скринкастинг в Linux

Вы всё ещё снимаете видео о том, как похекали сайт NASA, с помощью recordmydesktop? Не позорьтесь! Вот вам кошерная команда:

$ ffmpeg -f x11grab -s 1280x800 -r 25 -g 250 -i :0.0 -sameq -vcodec qtrle out.mov

1280x800 нужно заменить в соотвествии с разрешением экрана. На мой взгляд такой набор параметров обеспечивает оптимальное соотношение скорость/качество.