Monthly Archives: Квітень 2014

Віддалене керування Windows з Python

А хтось за допомогою пітона навчився керувати мікрохвильовкою.

А хтось за допомогою пітона навчився керувати мікрохвильовкою.

Зараз я знаю два способи робити це за допомогою WinRM – реалізації компанією Microsoft відкритого протоколу WS-Management (Якщо точніше то не WS-Management, а MS-WSMV але то надто тонкі деталі).

Перший спосіб – синхронна бібліотека нашого співвітчизника Алексея Діяна pywinrm. Другий – асинхронна бібліотека американського виробництва яка називається txwinrm, де tx означає Twisted. Плюс першої в тому, що з її коду я зліпив приклад виклику команди на віддаленому хості менш ніж за 300 рядків коду, замінивши urllib2 на requests. Думаю якщо ще трохи розберусь на дозвіллі – зліплю ще менший приклад і продокументую його маючи нормальний компактний опис нутрощів протоколу MS-WSMV.

А на роботі я маю проблеми з конфігурацією користувача для отакого віддаленого керування, але з адміністратором все працює після виконання на сервері звичайного winrm quickconfig.


Filed under: Кодерство Tagged: Python, windows

Фреймворк фільтра TSV (CSV)

Тут розкажу про модуль csv в Python, та як змусити його реагувати на кодування файлів.

TSV (значення розділені табуляцією) - це формат таблиці, яка зберігається в звичайному текстовому файлі і в якій кожен рядок – це рядок тексту, а стовпці розділяються символом табуляції. Його вміють відкривати всілякі програмки електронних таблиць, але перевага ще й в тому що його можна прочитати за допомогою звичайнісінького cat. Для прикладу табличка з групами крові (дані не перевірені, якщо ви сюди за ними, то краще спитайте лікаря):

bunyk@xubuntyk:~/tsv_filter_test$ cat blood.tsv 
Тип	Можливий донор для
A+	A+, AB+
O+	O+, A+, B+, AB+
B+	B+, AB+
AB+	AB+
A-	A+, A-, AB+, AB-
O-	A+, O+, B+, AB+, A-, O-, B-, AB-
B-	B+, B-, AB+, AB-
AB-	AB+, AB-


Давайте напишемо програму, яка візьме цю табличку, і перетворить її на табличку з парами дозволених переливань:

from csv_wrapper import transform_tsv # про це трохи далі 

def main():
    transform_tsv('blood.tsv', 'blood_pairs.tsv', transformation)

def transformation(row, num):
    if num == 0: # перший рядок - заголовок
        yield row # не чіпаємо
        return
    for recipient in row[1].split(', '): # пробігаємо по списку реципієнтів в другій колонці
        yield (row[0], recipient) # і віддаємо для кожної пари донор-реципієнт новий рядок

if __name__ == '__main__':
    main()

Отримуємо файл що починається так:

bunyk@xubuntyk:~/tsv_filter_test$ head blood_pairs.tsv 
Тип	Можливий донор для
A+	A+
A+	AB+
O+	O+
O+	A+
O+	B+
O+	AB+
B+	B+
B+	AB+
AB+	AB+

Тепер про те як працює transform_tsw. Вона використовує стандартну бібліотеку, але проблема зі стандартним модулем csv в тому що в python2 він дуже погано дружить з кодуваннями, і вимагає передавати йому байти, бо, бачте, з закінченнями рядків він сам хоче розібратись… Є ще модуль unicodecsv, який вирішує проблему з Python2, але в Python3 csv вже нормально спілкується Юнікодом, а open приймає кодування, тому там він не такий корисний.

Як варіант, ми використаємо адаптер описаний в книжці Porting to Python3. Тут його наводити не будемо, бо довгий (я його додав в бібліотеку butils), а просто розглянемо функцію transform_tsv, бо цікава:

def transform_tsv(src, dst, transformation, encoding='utf-8'):
    with UnicodeReader(src, encoding=encoding, delimiter='\t') as reader, \
        UnicodeWriter(dst, encoding=encoding, delimiter='\t') as writer:
        for i, src_row in enumerate(reader):
            for dst_row in transformation(src_row, i):
                writer.writerow(dst_row)

Тут за допомогою with ми відкриваємо одночасно два файли. А далі все очевидно. Проходимось по рядках першого, запускаємо фукцію трансформації і всі результати які вона видає записуємо у рядки другого.


Filed under: Кодерство Tagged: Python

Звідки в коді йде цей вивід? (вдосконалюємо логи)

Найважливіше питання для розробника якому треба пофіксити помилку, і він не знає де, але знає що в логах написано щось не те – хто саме й звідки ті логи пише.

Для вирішення такої проблеми я написав функцію watch_for_output, яка патчить sys.stdout та sys.stderr так що ті починають з кожним рядком що їм передають, виводити всі контактні дані того хто передає. Я помістив її в свою бібліотеку з всякими утилітами, тому використати її можна так:

import butils
butils.watch_for_output()

print 2 + 2

# OUT: test.py:4 print 2 + 2
# OUT: 4
# OUT: test.py:4 print 2 + 2 # print друкує ще перехід на новий рядок
# OUT: 

Біда тільки що коли вивід відбувається за допомогою не print, а наприклад якогось log.info, вийде таке:

2014-04-10 20:56:19,593 INFO zen.zenpython: Connecting to localhost:8789
/opt/zenoss/lib/python2.7/logging/__init__.py:863 stream.write(fs % msg)

Не дуже корисно… Але, ми бачимо в рядку лога таке: zen.zenpython. Це, скоріш за все, назва логера, а ми знаємо що логгер – сінглтон відносно назви (Note that Loggers are never instantiated directly, but always through the module-level function logging.getLogger(name). Multiple calls to getLogger() with the same name will always return a reference to the same Logger object. Logger objects). Тепер, ми можемо пропатчити його формат так, аби він друкував окрім всього іншого рядок і файл з якого викликаний:

import logging

FORMAT = '%(pathname)s:%(lineno)s %(message)s'
fmtr = logging.Formatter(FORMAT)
han = logging.StreamHandler()
han.setFormatter(fmtr)

log = logging.getLogger('zen.zenpython')
log.addHandler(han)

І ми отримуємо щось на зразок:

/opt/zenoss/Products/ZenHub/PBDaemon.py:602 Connecting to localhost:8789
2014-04-10 21:05:27,853 INFO zen.zenpython: Connecting to localhost:8789

Хей, тепер ми знаємо звідки можна починати копатись в коді!

Інші корисні поля, наприклад func (назва функції з якої відбувається логування) чи name (назва логера) описані в класі LogRecord.


Filed under: Кодерство Tagged: Python

Список акселераторів типів в PowerShell

$acceleratorsType = [type]::gettype("System.Management.Automation.TypeAccelerators")
# хитрий спосіб обійти той факт що цей тип чомусь недоступний.
$acceleratorsType::Add("accelerators", $acceleratorsType)
[accelerators]::Get | Format-Table -AutoSize
Key                   Value
---                   -----
int                   System.Int32
long                  System.Int64
string                System.String
char                  System.Char
bool                  System.Boolean
byte                  System.Byte
double                System.Double
decimal               System.Decimal
float                 System.Single
single                System.Single
regex                 System.Text.RegularExpressions.Regex
array                 System.Array
xml                   System.Xml.XmlDocument
scriptblock           System.Management.Automation.ScriptBlock
switch                System.Management.Automation.SwitchParameter
hashtable             System.Collections.Hashtable
type                  System.Type
ref                   System.Management.Automation.PSReference
psobject              System.Management.Automation.PSObject
pscustomobject        System.Management.Automation.PSObject
psmoduleinfo          System.Management.Automation.PSModuleInfo
powershell            System.Management.Automation.PowerShell
runspacefactory       System.Management.Automation.Runspaces.RunspaceFactory
runspace              System.Management.Automation.Runspaces.Runspace
ipaddress             System.Net.IPAddress
wmi                   System.Management.ManagementObject
wmisearcher           System.Management.ManagementObjectSearcher
wmiclass              System.Management.ManagementClass
adsi                  System.DirectoryServices.DirectoryEntry
adsisearcher          System.DirectoryServices.DirectorySearcher
psprimitivedictionary System.Management.Automation.PSPrimitiveDictionary
accelerators          System.Management.Automation.TypeAccelerators

Про деталі реалізації в C#, які змушують писати такий дивний код дивіться джерело.


Filed under: Кодерство Tagged: powershell