2010-10-22

Дружим pickle и __slots__

Баян, наверное, но я вот только сегодня столкнулся с такой проблемой и её решения в рунете не нашёл.
Делаем раз:
class Foo(object):
__slots__ = ['foo']
Делаем два:
import pickle
pickle.dumps(Foo()) # низя
И получаем ошибку: TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled.
Оказывается, всё до неприличия просто. По умолчанию, при сериализации, используется старинный протокол (номер ноль, pickle.dumps(Foo(), 0)), и вот он как раз и не может разрулить наши __slots__. В данной ситуации стоит использовать более продвинутый протокол (сейчас это номер 2, более старым версиям такой объект не по зубам):
import pickle
pickle.dumps(Foo(), -1) # годится
pickle.dumps(Foo(), 2) # тоже

p.s. Пытался сериализовать объекты Genshi.

2010-09-24

Калибруем APC Smart-UPS 750 (apcupsd)

Обычная проблема, новые аккумуляторы, индикатор заряда (даже полный, 5 делений) мерцает/мигает. Все в один голос — калибруй (утиль apctest), не думай. Но ведь любопытно, что подобное поведение ИБП означает, всё просто (хотя не просто было ответ найти), мерцающая шкала заряда (та что справа) говорит о том, что по глубочайшему убеждению агрегата расчётное время работы от батарей слишком мало, ну или на что-то там не хватит. Вы сталкивались с таким, но только когда оставалось одно-два деления (это скажем ~10% заряда).
Что меня смутило в момент калибровки, так это что к этому мерцанию прибавлялся короткий прерывистый, разрывающий душу, сигнал, и при этом калибрующая софтина не указывала уменьшение заряда, даже по прошествии многих минут (да и на самой железке индикатор оставался полным, т.е. все 5 делений горели).
$ sudo /etc/init.d/apcupsd stop
Stopping UPS power management: apcupsd.
$ sudo apctest


2010-09-24 04:26:54 apctest 3.14.8 (16 January 2010) debian
Checking configuration ...
Attached to driver: usb
sharenet.type = DISABLE
cable.type = USB_CABLE

You are using a USB cable type, so I'm entering USB test mode
mode.type = USB_UPS
Setting up the port ...
Hello, this is the apcupsd Cable Test program.
This part of apctest is for testing USB UPSes.

Getting UPS capabilities...SUCCESS

Please select the function you want to perform.

1) Test kill UPS power
2) Perform self-test
3) Read last self-test result
4) View/Change battery date
5) View manufacturing date
6) View/Change alarm behavior
7) View/Change sensitivity
8) View/Change low transfer voltage
9) View/Change high transfer voltage
10) Perform battery calibration
11) Test alarm
12) View/Change self-test interval
Q) Quit

Select function number:
Нервы не выдержали и я прервал процесс.
Select function number: 10

This test instructs the UPS to perform a battery calibration
operation and reports the result when the process completes.
The battery level must be at 100% and the load must be at least
10% to begin this test.

Battery level is 100% -- OK
Load level is 26% -- OK
Clearing previous self test result...CLEARED

The battery calibration should automatically end
when the battery level drops below about 25%.
This process can take minutes or hours, depending on
the size of your UPS and the load attached.

Initiating battery calibration...INITIATED

Waiting for calibration to complete...
To abort the calibration, press ENTER.

Battery level: 100%...............................................
..................................................................
..........
Но сегодня, набравшись как следует абсента, отключив монитор от UPS (что бы уменьшить нагрузку, тогда индикатор заряда переставал мерцать, так как уже времени автономной работы на что-то важное по мнению пепелаца хватало) решил добить эту калибровку.
Battery level: 91%..
Battery level: 79%
Battery level: 76%.
Battery level: 64%...
Battery level: 51%....
Battery level: 40%..
Battery level: 30%
CALIBRATION COMPLETED
Result of last self test: PASSED
Аллилуйя, братья и сестры.
Как вы заметили, после 100% идёт куча точек. Интерпретируйте каждую точку как 15 секунд времени, т.е. UPS и софтина терзали меня минут двадцать во время этой самой калибровки, убеждая вашего покорного слугу, что заряд всё ещё 100%. Но спустя нцать времени, железяка оживилась, индикатор заряда стал сомневаться в своей правоте и скакал между 2-4 делениями, да и apctest стала ему подыгрывать.
Короче, всё закончилось хорошо. Сейчас всё пучком и монитор работает через ИБП как и должно быть.

2008-12-27

Да будет сканер, в Linux

Несколько месяцев назад я занялся порочной практикой - самостоятельной сборкой ядер Linux. Процесс сам по себе не такой уж и сложный, но я, как человек разумный, решил переделать конфигурацию на свой лад, т.е. убрать всё лишнее, всё что не используется на моей домашней (по совместительству рабочей) машине. Тут и начались проблемы, перестало работать то одно устройство, то другое. Приходилось анализировать исходный конфиг (.config, хватило ума не удалять его, как и предыдущее рабочее ядро) со своим.
Вчера потребовался сканер (USB, Epson Perfection 2480 Photo). Боялся что ничего не заработает на моей сборке ядра и, разумеется, так и получилось. Перезагрузил старое ("оригинальное", из коробки Debian Etch, 2.6.18) ядро, всё нормально. Начал искать в чем причина.
$ sane-find-scanner
found USB scanner (vendor=0x04b8, product=0x0121) at libusb:003:002
Т.е. сканер есть, но его нет:
$ scanimage -L
device `v4l:/dev/video0' is a Noname BT878 video (AVerMedia TVPhone virtual device
То же самое, но под root:
$ sudo scanimage -L
Password:
device `v4l:/dev/video0' is a Noname BT878 video (AVerMedia TVPhone virtual device
device `snapscan:libusb:003:002' is a EPSON EPSON Scanner1 flatbed scanner

Вся закавыка заключается в том, что в своей компиляции я убрал устаревшую (DEPRECATED) опцию USB_DEVICE_CLASS. Если посмотреть описание этой опции, а еще ветку libsane udev rules for 2.6.22 в рассылке sane-devel, то становится ясно в чем дело и как поступить. Идем в /etc/udev/rules.d/z60_libsane.rules (в вашей системе, z60_libsane.rules может иметь другой префикс, не z60_). В начале этого файла должны быть строки:
ACTION!="add", GOTO="libsane_rules_end"                                                                                 
ENV{DEVTYPE}=="usb_device", GOTO="libsane_create_usb_dev"
SUBSYSTEM=="usb_device", GOTO="libsane_rules_begin"
SUBSYSTEM!="usb_device", GOTO="libsane_rules_end"

# Kernel >= 2.6.22 jumps here
LABEL="libsane_create_usb_dev"

# For Linux >= 2.6.22 without CONFIG_USB_DEVICE_CLASS=y
# If the following rule does not exist on your system yet, uncomment it
# ENV{DEVTYPE}=="usb_device", NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0664", OWNER="root", GROUP="root"

# Kernel < 2.6.22 jumps here
LABEL="libsane_rules_begin"
Нас интересует строка "If the following rule does not exist on your system yet, uncomment it". Как определить, что такого правила в системе еще нет. Во первых, сканер у нас под обычным пользователем всё еще не работает :-). Во вторых, анализируем выход такой команды:
$ grep -ir 'env{devtype}=="usb_device"' /etc/udev/rules.d/
/etc/udev/rules.d/z60_libsane.rules:ENV{DEVTYPE}=="usb_device", GOTO="libsane_create_usb_dev"
/etc/udev/rules.d/z60_libsane.rules:# ENV{DEVTYPE}=="usb_device", NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0664", OWNER="root", GROUP="root"
/etc/udev/rules.d/z60_libsane-extras.rules:ENV{DEVTYPE}=="usb_device", GOTO="libsane_extras_create_usb_dev"
/etc/udev/rules.d/z60_libsane-extras.rules:# ENV{DEVTYPE}=="usb_device", NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0664", OWNER="root", GROUP="root"
Очевидно, что такого правила нигде больше нет. Снимаем коментарий с известной строки, перезагружаем систему и радуемся, радуемся я сказал :-). У меня всё заработало:
$ scanimage -L
device `v4l:/dev/video0' is a Noname BT878 video (AVerMedia TVPhone virtual device
device `snapscan:libusb:003:002' is a EPSON EPSON Scanner1 flatbed scanner

2008-10-16

Тормозим оптический привод

Надоел шум нового супер-быстрого CD-ROM (DVD-ROM или пишущего комбайна) при просмотре фильма? Ограничиваем его скорость самым простым способом, который может прийти в голову:
$ eject -x8 /media/cdrom0
Где -x8 — скорость работы привода (при x32 мой привод шумит значительно меньше, почти не заметно, а при x16 его не слышно вообще, вы можете попробовать другие скорости), дальше — точка монтирования вашего устройства, либо это само устройство (/dev/hdc). Я пробовал работать только с точкой монтирования, хотя уверен, что и ссылка непосредственно на устройство, должна сработать. В любом случае, вам поможет гугль и:
$ man eject
Если я всё правильно помню, то первая скорость (CD-ROM Mode 1) это 150 килобайт/сек., т.е. 150 килобайт * 8 бит = 1.2 мегабита. Качество традиционных фильмов MPEG4 колеблется в пределах 700 килобит/сек. + звук, редко больше 128 килобит/сек. итого у нас выходит 828 килобит. Согласитесь, даже для первой скорости, запас очень не плохой. Вывод такой что вы можете не париться и ставить первую или вторую скорость, врядли какой-либо привод будет шуметь на такой скорости, а если диск не испорчен, вы посмотрите его нормально. Да и в повседневной жизни скорость можно уменьшать. Уверен что не всегда нужны обороты в x48 или больше, ведь основной проблемой будут не обороты диска, а перемещение головки — скорость вращения диска дает преимущества только при его последовательном чтении. В сети вы можете найти описание другого утиля для управление скоростью привода, но ни один из них, как и этот способ, не работают при использовании DVD-диска, не знаю в чем дело, но DVD, что очевидно, вращаются с максимальной скоростью и продолжают шуметь, что бы вы не делали.

p.s. Забыл сказать, что речь идет о Linux :-).

2008-10-10

Twisted-клиент и веб-прокси

Как это не парадоксально, но Twisted в своем арсенале не имеет средств для работы с HTTP через прокси-сервер. Я наклепал небольшой код, который решает эту проблему. Сделано не бог весть как, но если кому-то очень нужно, то сгодится и этот код.
from twisted.web import client as _twc


class HTTPPageGetter(_twc.HTTPPageGetter):

def sendCommand(self, command, path):
if self.factory.proxy:
from twisted.web.http import urlparse
from urlparse import urlunparse

domain = urlparse(self.factory.url)[:2]
path = urlparse(path)[2:]
path = urlunparse(domain + path)

self.transport.write('%s %s HTTP/1.0\r\n'%(command, path))

def handleStatus_301(self):
location = self.headers.get('location')
if not location:
self.handleStatusDefault()
return
url = location[0]

if self.followRedirect:
self.factory.connect(url)

else:
from twisted.python.failure import Failure
from twisted.web.error import PageRedirect

self.handleStatusDefault()
self.factory.noPage(Failure(
PageRedirect(self.status, self.message,
location = url)))

self.quietLoss = 1
self.transport.loseConnection()



class HTTPClientFactory(_twc.HTTPClientFactory):

protocol = HTTPPageGetter
proxy = None

def __init__(self, url, **kwargs):
self.proxy = kwargs.pop('proxy', None)
_twc.HTTPClientFactory.__init__(self, url, **kwargs)

def connect(self, url = None):
if url:
self.setURL(url)

if self.proxy is not None:
try:
host, port = self.proxy.split(':')
host, port = str(host), int(port)
except:
raise Exception(
'Incorrect proxy address "%s"'%self.proxy)

else:
host, port = self.host, self.port

from twisted.internet import reactor

if self.scheme == 'https':
from twisted.internet.ssl import ClientContextFactory

contextFactory = ClientContextFactory()
reactor.connectSSL(host, port, self, contextFactory)

else:
reactor.connectTCP(host, port, self)
Этот код вырван из контекста (моего проекта), так что я его не тестировал, если будут ошибки — обращайтесь, разберемся.
Для работы с этой HTTPClientFactory теперь не стоит использовать вызов reactor.connectTCP, для установления соединения вызовите метод connect этой фабрики, а так же, при создании фабрики, не забудьте указать какой прокси вы хотите использовать:
factory = HTTPClientFactory('http://python.su/',
proxy = 'localhost:3128')
factory.connect()