2017/06/23

Przykładowa praca zaliczeniowa

W 2012 roku, prowadząc zajęcia na WSB zamieściłem na swojej stronie przykładową pracę zaliczeniową. Strona ją zawierająca była od tamtego czasu najczęściej odwiedzaną podstroną. W związku z tym zamieszczam ją i tutaj - by było ją łatwiej odnaleźć. Tak jak i na tamtej stronie zwracam uwagę:
,,Bez konsultacji z prowadzącym nie zawsze będą Państwo wiedzieć, który styl jest ,,lepszy'', a który ,,gorszy''. Praca którą udostępniam została już ,,zaliczona'', można więc przypuszczać, że jest pracą przynajmniej akceptowalną.''
Podkreślam - ta praca została już ,,zaliczona'', nie można jej więc oddawać jako własnej. Jest ona moją własnością, proszę więc jej nie redystrybuować inaczej, niż z linkiem do wrobelmaciek.info lub wrobelmaciek.blogspot.com.

Tutaj link do pracy udostępnionej przez google drive: https://drive.google.com/file/d/0B91KeMYac8JcZHhHMVNXVGVsREE/preview


openssl - Jak wygenerować samopodpisany certyfikat jednolinijkowcem (oneliner self-signed certificate)

Potrzebujesz samopodpisany certyfikat (self-signed certificate)? Możesz wygenerować go korzystając z openssl-a, dostępnego na praktycznie każdym systemie* poniższym jednolinijkowcem:
openssl req -subj '/CN=twojadomena/O=nazwa twojej firmy/C=PL'  -new -newkey rsa:2048 -days 365 -nodes -x509 -sha256 -keyout server.key -out server.crt
W plikach server.key i server.crt znajdzie się klucz prywatny podpisujący certyfikat i sam certyfikat, odpowiednio.


* na większości linuxów openssl jest zainstalowany domyślnie. Na Windowsie można z niego korzystać przy pomocy Cygwina, lub też zainstalować samodzielne binaria - zob. na stronce projektu.

2017/02/13

Przeszukiwanie dokumentów XML przy pomocy XPath

Przeszukiwanie dokumentów XML przy pomocy XPath

Dokumenty XML są standardem przechowywania i przekazywania danych. Xpath natomiast jest jedną z podstawowych metod przeszukiwania XMLi. Jak więc w Pythonie przeszukiwać xmle?
Python posiada w bibliotece standardowej parser XML - moduł xml. Jednak moduł ten ma ograniczone wsparcie dla XPath (zobacz xpath support na docs.python.org). Bogatsze wsparcie dla XPath zapewnia biblioteka lxml, oparta na libxml2 i libxslt - dobrze rozwiniętych i dostępnych na wiele platform bibliotek. lxml można łatwo zainstalować przy pomocy pip-a, jest wśród standardowych pakietów anacondy, a ze względu na zależności często jest zainstalowany także na linuxowych systemach (zależy od niego np. Inkscape i Cinnamon).
Poniższy program, kompatybilny z Python 3, przedstawia przykłady użycia lxml do parsowania XML z wykorzystaniem XPath.

'''przykladowy program parsujacy XMLe'''
from lxml import etree

# przykladowy xml wczytany bezposrednio z lancucha bajtow.
xml = etree.fromstring(b'''<?xml version="1.0" encoding="UTF-8"?>
<ksiazki>
  <ksiazka>
    <tytul lang="pl">Pan Tadeusz</tytul>
    <autor>A. Mickiewicz</autor>
    <rok>2012</rok>
    <cena>31.99</cena>
  </ksiazka>
  <ksiazka>
    <tytul lang="pl">Antygona</tytul>
    <autor>Sofokles</autor>
    <rok>2006</rok>
    <cena>5.99</cena>
  </ksiazka>
  <f:ksiazka xmlns:f="https://wrobelmaciek.blogspot.com/ksiazki">
    <f:tytul lang="pl">Emocjonalne wampiry w pracy</f:tytul>
    <f:autor>A. B. Bernstein</f:autor>
    <f:rok>2014</f:rok>
    <f:cena>37.90</f:cena>
  </f:ksiazka>
</ksiazki>
''')

#metoda xpath zwraca liste elementow spelniajacych kryteria
#elementy sa objektami, mozna odczytac ich tekst, wziac przodka, 
#atrybuty itd.
print('autorzy ksiazek i jezyk tytulu')
for el in xml.xpath('.//autor'):
    print('{0}:{1}'.format(el.text,
                           el.getparent().find('tytul').get('lang'), ))


#lxml obsluguje tez bardziej zlozone zapytania
print('tytuly ksiazek zaczynajace sie na A:')
print('\n'.join([x.text for x in  
    xml.xpath('.//*[name()="tytul" and starts-with(text(),"A")]')]))

#lxml obsluguje tez przestrzenie nazw (namespaces)
print('''autorzy o nazwiskach zaczynajacych sie na A, 
niezależnie od przestrzeni nazw:''')
print('\n'.join(['%s %s'%(x.tag,x.text,) for x in  
    xml.xpath('.//*[local-name()="autor" and starts-with(text(),"A")]')]))

print('''ksiazki o autorach zaczynajacych sie na A, 
z przestrzeni nazw {https://wrobelmaciek.blogspot.com/ksiazki}:''')
print('\n'.join(['%s %s'%(x.tag,x.text,) for x in
    xml.xpath('.//g:autor[starts-with(text(),"A")]',
    namespaces={'g':'https://wrobelmaciek.blogspot.com/ksiazki'})]))


#jesli nie potrzebujemy zlozonych zapytan, mozemy uzyc tez findall
#kompatybilnego z xml.ElementTree z biblioteki standardowej pythona
print('autorzy ksiazek i jezyk tytulu - findall')
for el in xml.findall('.//autor'):
    print('{0}:{1}'.format(el.text,
                           el.getparent().find('tytul').get('lang'), ))


#findall obsluguje tylko najprostsze zapytania XPath
try:
    print('to nie zadziala')
    for el in xml.findall('.//*[name()="autor"]'):
        print('{0}:{1}'.format(el.text,
                               el.getparent().find('tytul').get('lang'),
        ))
except SyntaxError:
    print('zbyt skomplikowany predykat')

2017/01/23

Jak działa algorytm map-reduce?

Map, Reduce i wielowątkowość w Pythonie

MapReduce jest własnościową platformą Google do przetwarzania równoległego. Nazwa sugeruje związek z operacjami map i reduce, jednak przeglądając przykłady, spotkałem się od razu z pojęciami mapperów, reduktorów, partycjonerów itd, bezpośrednio powiązanych z implementacjami realizującymi algorytm (jak np. hadoopowy mapreduce).
Poniższy przykład ilustruje podstawy algorytmu przy pomocy elementarnych elementów biblioteki standardowej Pythona - metody map z multiprocessing.pool i reduce z functools (w Pythonie 2 będącej funkcją podstawową).
Program wieloprocesowo zlicza wystąpienia poszczególnych znaków w zadanym ciągu liter.
'''prosty wielowatkowy program
ilustrujacy idee algorytmu map-reduce'''

import multiprocessing
from functools import reduce

def policz_litery(lancuch):
    '''funkcja map'''
    unikalne_znaki = set(lancuch)
    wystapienia = { k:0 for k in unikalne_znaki }
    for litera in lancuch:
        wystapienia[litera] += 1
    return wystapienia

def polacz_slowniki(slownik1, slownik2):
    '''funkcja redukuje dwa slowniki z licznikami liter do jednego slownika'''
    wynik = slownik1
    for litera in slownik2.keys():
        if litera in wynik:
            wynik[litera] += slownik2[litera]
        else:
            wynik[litera] = slownik2[litera]
    return wynik

#tekst w ktorym policzymy wystapienia liter
lorem_ipsum = '''Lorem ipsum dolor sit amet, 
consectetur adipiscing elit, sed do eiusmod 
tempor incididunt ut labore et dolore magna aliqua. 
Ut enim ad minim veniam, quis nostrud exercitation 
ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in 
voluptate velit esse cillum dolore eu 
fugiat nulla pariatur. Excepteur sint 
occaecat cupidatat non proident, sunt in 
culpa qui officia deserunt mollit anim id est laborum.'''

#obliczenia beda wykonane niezaleznie na zdaniach (rozdzielonych kropkami)
zdania = lorem_ipsum.split('.')

#wykonujemy zadanie policz_litery na poszczegolnych zdaniach (mapowanie)
pulaworkerow = multiprocessing.Pool()
policzone_zdaniami = pulaworkerow.map(policz_litery,zdania)

#redukujemy otrzymane wyniki czesciowe przy pomocy funkcji polacz_slowniki
zredukowane = reduce(polacz_slowniki,policzone_zdaniami)
print(zredukowane)

Przeszukiwanie katalogów

Przeszukiwanie katalogów w Python

Jedną z zalet Python-a jest jego wieloplatformowość. Bardzo często da się w tym języku napisać skrypt lub program, który bez zmian będzie działał zarówno na Windowsach, jak i na różnego rodzaju unixo-pochodnych systemach (linuxach, aixach czy solarisach).
Poniższy programik pozwala przejść strukturę katalogów w poszukiwaniu plików których nazwy pasują do zadanego wyrażenia regularnego. Zadanie to łatwo zrobić zarówno w Powershellu, jak i bashu, jednak programik ten łatwo można wykorzystać w większych projektach, w których potrzebujemy przeszukiwać system plików.
Program wykorzystuje funkcję os.walk do trawersowania systemu plików i re.match do dopasowania wzorca. Nie wymaga więc instalowania niczego spoza biblioteki standardowej Pythona. Kolejne znalezione pliki zwracane są przez generator, więc programik nie jest bardzo zasobożerny (przynajmniej w kwestii pamięci).
'''program przechodzący przez katalogi podane w argumencie
w poszukiwaniu plikow pasujacych do wyrazenia regularnego'''
import os
import re
import sys
def przeszukaj_katalogi(katalog_glowny,wzorzec):
    '''funkcja generujaca pliki w oparciu o wzorzec w zadanym katalogu'''
    for katalog,katalogi,pliki in os.walk(katalog_glowny):
        for plik in pliki:
            if re.match(wzorzec,plik):
                yield(katalog+os.sep+plik)

#przyklad uzycia
if __name__=='__main__':
    if len(sys.argv)<2:
        print('podaj wzorzec plikow ktore poszukujesz')
        sys.exit()
    elif len(sys.argv)<3:
        katalogi = ['.']
        wzorzec = sys.argv[1]
    else:
        wzorzec = sys.argv[1]
        katalogi = sys.argv[2:]

    for katalog in katalogi:
        for plik in przeszukaj_katalogi(katalog,wzorzec):
            print(plik)

Obliczanie wartości własnych

Obliczanie wartości własnych i wektorów własnych macierzy w Python

Jednym z pierwszych zastosowań, do których wykorzystywałem języka Python, była praca z dużymi macierzami opisującymi konfiguracje elektronów. Python posiada doskonałą bibliotekę do obliczeń numerycznych - numpy. Praca z macierzami staje się wyjątkowo prosta:) Poniższy program pokazuje, jak obliczyć wartości własne macierzy (numpy.linalg.eigvals) oraz jak obliczyć wartości i wektory własne macierzy (eig).
'''programik oblicza wartosci wlasne (i ewentualnie wektory wlasne) macierzy'''

import numpy as np
#przykladowa macierz (o losowych elementach)
mtx = np.random.rand(1000,1000)

#z bedzie zawieralo wartosci wlasne
z=np.linalg.eigvals(mtx)
#z=np.linalg.eig(mtx)

Jak widać programik jest wyjątkowo prosty i łatwy w zrozumieniu. To, co dzieje się pod spodem wcale nie jest już takie proste.
Uwaga - obliczanie wektorow wlasnych wymaga dodatkowych obliczeń i dostepnej pamieci. Jeśli interesują Cię szczegóły, to zobacz dokumentację silnika numpy, czyli lapacka.

Interakcja ze schowkiem

1 Interakcja ze schowkiem w Windows

Poniżej zamieszczony programik pozwala posortować tekstową zawartość schowka w Windows. Jest prostym przykładem komunikacji ze schowkiem.
Wymaga zainstalowania biblioteki win32clipboard, dostarczanej razem z pywin32.
import win32clipboard
win32clipboard.OpenClipboard()
clipboard_data = win32clipboard.GetClipboardData()

sorted_data = '\n'.join(sorted(clipboard_data.splitlines()))

win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(sorted_data)
win32clipboard.CloseClipboard()

Wieloprocesowy ping i xlsx

1Wieloprocesowy ping i obsługa xlsx w Python

Korzystając z biblioteki openpyxl można w pythonie całkiem wygodnie korzystać z plików w formacie xlsx. Poniższy programik ilustruje zarówno czytanie, jak i pisanie do xlsx-a.
Program jest też prostym przykładem wieloprocesowego przetważania. Wykorzystuje bibliotekę standardową multiprocessing, żeby uruchomić w procesach potomnych systemowe polecenie ping (przy pomocy subprocess.Popen). Hosty wczytane z pliku xlsx po kolei są ,,pingane'', a wyniki ping-a zapisywane są do pliku. Jeżeli część pakietów nie powróciła z celu, to komórka arkusza jest kolorowana.
'''program wysyła ping-a asynchronicznie do hostow
podanych jako argument skryptu i zapisuje do xlsx-a
wynik dla kazdego hosta.

Hosty do ktorych nie wszystkie ping-i doszly
zostana pokolorowane na czerwono.
'''

import multiprocessing
import re
import subprocess
import sys
from openpyxl import load_workbook, Workbook
from openpyxl.styles import PatternFill

def pingHost(hostname):
        try:
                v=subprocess.check_output('ping.exe -n 1 {0}'.format(hostname),shell=True)
        except subprocess.CalledProcessError as e:
                v=e.output              
        #zbierz lancuch zawierajacy wyniki pinga i podziel go na linie
        ping_output = v.decode(errors='ignore').splitlines()
        straty = '\n'.join([x.strip() for x in ping_output if re.search('straty',x)])
        ip = '\n'.join([re.sub('[^0-9.]','',x) for x in ping_output if re.search('badania',x)])
        return {'ip':ip,'straty':straty,'hostname':hostname}

def write_hosts(filename,host_list):
        '''zapisz otrzymane wyniki do xlsx'''
        wb = load_workbook(filename)
        ws = wb.active
        ws.cell(row=1,column=2).value='ip'
        ws.cell(row=1,column=3).value='losses'
        for i,host in enumerate(host_list):
                if(host['straty']!='(0% straty),'):
                        ws.cell(row=2+i,column=1).fill=red_pattern_fill()
                ws.cell(row=2+i,column=2).value=host['ip']
                ws.cell(row=2+i,column=3).value=host['straty']
        wb.save(filename)

def read_hosts(filename):
        '''wczytaj hosty do pingania z xlsx-a
        z pominieciem 1 wiersza (naglowka)'''
        wb = load_workbook(filename)
        ws = wb.active
        rows_to_read = range(2,ws.max_row+1)
        return [ws.cell(row=row,column=1).value for row in rows_to_read]

def red_pattern_fill():
        '''pomocnicza funkcja definiujaca czerwone wypelnienie'''
        return PatternFill(start_color='FFFF0000', end_color='FFFF0000', fill_type='solid')

if __name__ == '__main__':
        hosty=read_hosts(sys.argv[1])
        pool=multiprocessing.Pool()
        v=pool.map(pingHost,hosty)
        write_hosts(sys.argv[1],v)

Wyrażenia regularne w sqlite3

1 obsługa wyrażeń regularnych w sqlite3

Sqlite3 umożliwia wykorzystywanie funkcji regexp w zapytaniach SQL. Żeby to jednak zrobić trzeba stworzyć funkcję regexp przez metodę createfunction połączenia do bazy sqlite. Poniżej przykładowy program.
import sqlite3
import re


def regexp(expr, item):
    """pomocnicza funkcja regexp dla sqlite3"""
    reg = re.compile(expr)
    return reg.search(item) is not None


if __name__ == '__main__':
    conn = sqlite3.connect(':memory:')
    conn.create_function("REGEXP", 2, regexp)

    miesiace = [('styczen',31),
                ('luty',28),
                ('marzec',31),
                ('kwiecien',30),
                ('maj',31),
                ('czerwiec',30),
                ('lipiec',31),
                ('sierpien',31),
                ('wrzesien',30),
                ('pazdziernik',31),
                ('listopad',30),
                ('grudzien',31)]

    conn.execute('CREATE TABLE miesiace (miesiac,dni)')
    conn.executemany('INSERT INTO miesiace VALUES(?,?)',miesiace)
    conn.commit()
    miesiace_z_e = conn.execute('SELECT * FROM miesiace WHERE miesiac REGEXP "(?i)r"').fetchall()
    miesiace_bez_e = conn.execute('SELECT * FROM miesiace WHERE NOT miesiac REGEXP "(?i)r"').fetchall()
    print('*'*25, 'miesiace z litera r:','*'*25)
    print('\n'.join(['%s:%i'%(krotka) for krotka in miesiace_z_e]))
    print('*'*25, 'miesiace bez litery r:','*'*25)
    print('\n'.join(['%s:%i'%(krotka) for krotka in miesiace_bez_e]))

Obsługa plików tar

1 obsługa plików tar

Biblioteka standardowa Pythona umożliwia (także pod Windowsem) pakowanie i rozpakowywanie archiwów tar (tar.gz i tar.bz2 też). Poniżej prosty przykład jak użyć tej biblioteki do pakowania listy plików.

import tarfile
import sys
from os import sep as separator
from os.path import exists


def ustaw_uprawnienia(tarinfo):
        tarinfo.mode = 0o444
        tarinfo.uid = 0
        tarinfo.gid = 0
        tarinfo.uname = 'root'
        tarinfo.gname = 'root'
        return tarinfo

def pakuj(plik_tar,lista_plikow,mode='w:gz'):
        with tarfile.open(plik_tar,mode) as tar:
                for plik in lista_plikow:
                        #usun sciezke z docelowej nazwy pliku
                        gdzieslash = plik.rfind(separator)+1
                        krotka_nazwa = plik[gdzieslash:]
                        tar.add(plik,krotka_nazwa,filter = ustaw_uprawnienia)

if __name__ == '__main__':
        if(len(sys.argv)<2):sys.exit('podaj nazwe archiwum oraz liste plikow do spakowania')
        wynikowy_tar = sys.argv[1]
        if(exists(wynikowy_tar)):sys.exit('plik archiwum nie moze istniec')
        lista_plikow = sys.argv[2:]
        pakuj(wynikowy_tar,lista_plikow)

Powyższy program nadaje uprawnienia 0444 i ustawia właściciela/grupę wszystkich plików dodawanych do archiwum na root.root. Dodatkowo – usuwane są wszystkie informacje o katalogach (scieżka jest usuwana w linijkach 19-20).

2017/01/21

Dzielenie kodu na porcje

1 Dzielenie kodu na porcje

W jaki sposób podzielić bardzo dużą listę na mniejsze porcje? Można do tego wykorzystać funkcjonalność generatorów.
def porcje(lista, dlg_porcji):
    """funkcja-generator dzielaca liste na mniejsze porcje"""
    for i in range(0, len(lista), dlg_porcji):
        yield lista[i:i + dlg_porcji]

if __name__ == '__main__':
        lista = [x for x in range(0,100)]
        for i,p in enumerate(porcje(lista,10)):
                print('{0}-ta porcja:'.format(i),p)