Pokazywanie postów oznaczonych etykietą python. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą python. Pokaż wszystkie posty

31 grudnia 2023

[python] Random

Jednolinijkowiec na wygenerowanie 20 losowych bajtów (Ubuntu 18.04).
import random as r; r.seed(1337); " ".join([f'{b:02x}' for b in r.randbytes(20)])
Wynik:
'03 0d 25 9e 7b e3 ef ec a5 17 84 88 cd b1 ba b5 ff 3c a8 5d'

16 lipca 2020

[python] asyncio - Asynchronous I/O

Asynchronieczne funkcje w Pythonie nazywane są corutines (poprzedza jest słowo kluczowe async albo są udekorowane @asyncio.coroutine). Nie można ich wołać jak zwyczajnych funkcji, trzeba skorzystać ze słowa kluczowego await (podobne do yield) i można to robić tylko wewnątrz innych corutines. await przerywa działanie i oddaje sterowanie do "event loop", które zajmuje się zarządzeniem (przekazywaniem sterowania do corutines), i w której są one rejestrowane. Przydatne linki: Biblioteka posiada całe mnóstwo funkcji, dla mnie najważniejsze to:
  • create_task() kolejkuje zadanie
  • run_until_complete() uruchomienie corutine (i wszystkie inne zakolejkowane do tej pory zadania) i czeka aż się zakończy (to konkretna). Jeżeli będą jakiś inne zadania w stanie oczekiwania to run_until_complete() nie będzie na nie czekać.
  • run_forever() uruchamia wszystkie zakolejkowane zadania
Przykład:
import asyncio

async def short_task():
    print('short_task before')
    await asyncio.sleep(2)
    print('short_task after')


async def print_task():
    print('print_task')


async def long_task():
    print('long_task before')
    await asyncio.sleep(5)
    print('long_task after')


async def draw_task():
    print('draw_task')


def main():
    loop = asyncio.get_event_loop()

    loop.create_task(print_task())
    loop.create_task(long_task())

    loop.run_until_complete(short_task())
    loop.run_until_complete(draw_task())

    loop.close()


if __name__ == '__main__':
    main()
Program czeka aż zakończą się dwa zadania: short_task i draw_task, wcześniej uruchamiająć long_task. Ponieważ short_task i draw_task kończą się szybciej dostajemy ostrzeżenie, o wciąż działającym long_taks.
print_task
long_task before
short_task before
short_task after
draw_task
Task was destroyed but it is pending!
task: <Task pending coro=<long_task() done, defined at /home/beru/python_asyncio/run_until.py:13> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f47d9b217d0>()]>>
Bardziej zaawansowany przykład serwera/czatu. Tutaj mamy do czynienia z trzema rodzajami zdarzeń: uruchomienie serwera (otwarcie portów i nasłuchiwanie), oczekiwanie na tekst na stdio oraz oczekiwanie na nadejście wiadomości od klienta. Samo oczekiwanie na tekst składa się z dwóch zdarzeń: pojawienie się tekstu na stdio i zapisanie go do kolejki, oraz odczytanie, gdy coś w tej kolejce się znajduje.

Jest tu kilka kwiatków asyncio, jak np. sposób przekazywanie parametrów do obiektu Chat (przez lambdę). Ale najbardziej dokuczliwym (i dalej nie mam pewności czy zrobiłem to poprawnie) jest sposób zatrzymania programu. Po otrzymaniu komendy "exit" Chat ustawia future na True, co z kolei anuluje najpierw wszystkie zadania, następnie stopuje pętle, a na końcu pętla ta jest jeszcze zatrzymywana.
Jeżeli program jest zabijany przez Ctrl+C, to ustawienie future w obsłudze wyjątku nie zadziałała. Koniecznym stało się wywołanie explicit cancel_all_task(). Samo anulowanie zadań jest też zdaje się być asynchroniczne, więc stop() nie może być zawołane za wcześnie.
import sys
import asyncio


def main():
    queue = asyncio.Queue()
    loop = asyncio.get_event_loop()

    # Start monitoring the fd file descriptor for read availability and invoke
    # callback with the specified arguments once fd is available for reading
    loop.add_reader(sys.stdin, got_stdin_data, queue)

    fut = loop.create_future()
    fut.add_done_callback(cancel_all_task)

    coro = loop.create_server(lambda: Chat(loop, queue, fut), '127.0.0.1', 7777)
    server = loop.run_until_complete(coro)

    # Run until Ctrl+C is pressed or loop is stopped
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        print('[+] Keyboard exception')
        cancel_all_task()

    # Stop server. Closing listening sockets it's done asynchronously, so
    # wait_closed() need to be used to ensure.
    server.close()
    loop.run_until_complete(server.wait_closed())

    loop.close()


def got_stdin_data(queue):
    loop = asyncio.get_event_loop()
    loop.create_task(queue.put(sys.stdin.readline()))


def cancel_all_task(result=None):
    print('[+] Cancel all tasks')
    loop = asyncio.get_event_loop()
    for task in asyncio.Task.all_tasks():
        task.cancel()
    loop.create_task(stop_loop())


async def stop_loop():
    print('[+] Stop loop')
    loop = asyncio.get_event_loop()
    loop.stop()


class Chat(asyncio.Protocol):
    def __init__(self, loop, queue, fut):
        self.loop = loop
        self.queue = queue
        self.fut = fut

    def connection_made(self, transport):
        peername = transport.get_extra_info('peername')
        print('[+] Connection from:', peername)
        self.transport = transport
        self.loop.create_task(self._wait_for_stdin_data())

    def connection_lost(self, exc):
        print('[+] Connection lost')
        self.fut.set_result(True)

    def data_received(self, data):
        message = data.decode()
        print('[+] Data received: {!r}'.format(message))

        if message.strip() == "exit":
            self.fut.set_result(True)

    def _send_reply(self, reply):
        print('[+] Data send: {!r}'.format(reply))
        self.transport.write(reply.encode())
        self.loop.create_task(self._wait_for_stdin_data())

    async def _wait_for_stdin_data(self):
        reply = await self.queue.get()
        self._send_reply(reply)


if __name__ == '__main__':
    main()
W celu połączenia się z serwerm:
nc 127.0.0.1 7777
Działanie:
[+] Connection from: ('127.0.0.1', 45260)
[+] Data received: 'asdf\n'
[+] Data received: 'exit\n'
[+] Cancel all tasks
[+] Stop loop

7 marca 2020

OpenCV - budowanie ze źródeł

Ostatnio musiałem skomplikować OpenCV w wersji Debug. Zresztą wersja dostępna w repozytoriach Ubuntu (19.10) to obecnie 3.2, trochę stara, w porównaniu najnowszą 4.2. Pomocny link:
A tu moje (skondensowane) kroki, żebym nie zapomniał:
# Katalog roboczy
mkdir ~/opencv_workspace
cd ~/opencv_workspace
git clone https://github.com/opencv/opencv.git
git clone https://github.com/opencv/opencv_contrib.git

# Instalacja virualenv dla Python-a. Przyda się numpy i scipy
virtualenv -p python3 venv
source venv/bin/activate
pip install numpy
pip install scipy

# Konfiguracja za pomocą CMake. 
# Wszystko co potrzebne do budowania znajdzie się w katalogu build, 
# a zainstalowane zostanie do katalogu $VIRTUAL_ENV/local/
cd opencv
mkdir build
cmake -B build/ -D CMAKE_BUILD_TYPE=Debug \
    -D OPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules/ \
    -D CMAKE_INSTALL_PREFIX=$VIRTUAL_ENV/local/ \
    -D PYTHON_EXECUTABLE=$VIRTUAL_ENV/bin/python \
    -D PYTHON_PACKAGES_PATH=$VIRTUAL_ENV/lib/python3.7/site-packages \
    -D INSTALL_PYTHON_EXAMPLES=ON

# Kompilacja i instalacja (do katalogu $VIRTUAL_ENV/local/)
cd build
make -j4
make install
Przykładowy program
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat grayImg = cv::imread("color.png", cv::IMREAD_GRAYSCALE);
    cv::imwrite("gray.png", grayImg);
}
Kompilacja:
cd ~/opencv_workspace
g++ -I./venv/local/include/opencv4 -L./venv/local/lib -Wl,-rpath=./venv/local/lib \
    main.cpp \
    -lopencv_core \
    -lopencv_imgcodecs \
    -lopencv_imgproc
Wynik:



1 lipca 2018

Jupyter VPython 7

Jakoś tak się złożyło, że kiedy ja zabrałem się za bibliotekę VPython w 2016 roku, projekt był właśnie porzucony, a developerzy skupili się na wersji opartej o Jupytera i GlowScript-a. GlowScript-a umożliwia, kompilację kodu python-a do JavaScipt-u. Powstał nawet bardzo fajny serwis trinket.io, który zamyka cały proces tworzenia w przeglądarce. Jako narzędzie edukacyjne, używanie VPythona stało się jeszcze wygodniejsze. Wraz z wersją Ubuntu 18.4, paczka VPythona została usunięta z repozytorium.
Trochę szukania i okazuje się, że nie jest tak źle. Strona VPython informuje o projekcie IVisual, jednak stackoverflow sugerował wersję VPython. Nie wiem jak jest między nimi różnica, zacząłem od tego drugiego i przy nim pozostałem.
Instalacja poszła z pewnymi problemami. Zanim zostanie zainstalowany VPython, trzeba zainstalować paczkę Jupyter (w tej kolejności), jest to spowodowane jakimś bugiem:
mkdir vpython3
cd vpython3/
virtualenv -p python3 venv
source venv/bin/activate
pip3 install jupyter
pip3 install vpython
W API pojawiły się pewne drobne zmiany, dlatego postanowiłem przepisać standardowy przykład "bouncing ball" na nową wersję. Przy okazji odkryłem na stronie projektu informacje o module povexport do przechwytywania obrazu animacji. Wcześniej przechwytywałem obraz z ekranu (za pomocą PIL) i wycinałem obszar w którym pojawia się okno animacji.
Przykład:
import vpython as vp
import povexport

scene = vp.canvas(title='3D scene', 
                  x=0, y=0, width=400, height=400, 
                  center=vp.vector(0, 0, -0), autoscale=False)
vp.display(scene)

starting_height = 4
floor = vp.box(pos=vp.vector(0, 0, 0), length=4, height=0.5, width=4, color=vp.color.green)
ball = vp.sphere(pos=vp.vector(0, starting_height, 0), radius=1, color=vp.color.red)
ball.velocity = vp.vector(0, -1, 0)
dt = 0.01

frame = 0
while 1:
    vp.rate(100)
    ball.pos = ball.pos + ball.velocity*dt
    if ball.pos.y < ball.radius:
        ball.velocity.y = abs(ball.velocity.y)
    else:
        ball.velocity.y = ball.velocity.y - 9.8*dt
    
    file_name = 'img-%04d.pov' % frame
    inclist = ['colors.inc', 'stones.inc', 'woods.inc', 'metals.inc']
    povexport.export(scene, filename=file_name, include_list=inclist)
    frame += 1
        
    if ball.pos.y > starting_height:
        break
Co się zmieniło (dla mnie):
  • nowy moduł importu (zamiast visual jest vpython)
  • parametry przyjmują explicit obiekt vector i nie tolerują trójelementowej krotki
  • funkcji display zmieniła nazwę na canvas
Słów kilka o povexport. Sprytna biblioteka, która zapisuje informacje o bryłach i powierzchni w tekstowym języku/formacie SDL (Scene Description Language). Dane te następnie można przetworzyć za pomocą programu jak povray na format graficzny np. png i dalej skonstruować animację. Daje to całkiem ciekawe możliwości, bowiem VPython w założeniu oferuje tylko proste możliwości graficzne, povray daje dużo więcej możliwości jeżeli chodzi o renderowanie i ray tracing.
Nie obyło się bez małej ingerencji w kod, gdyż domyślnie obliczania ustawień odległości kamery w povexport były zbyt małe. Może coś robiłem nie tak, ostatecznie zmieniłem jedną linijkę.
# ...
# cpos = 1.5*displayscale*canv.camera.pos # 1.5 is a not understood fudge factor
  cpos = 10*displayscale*canv.camera.pos
# ...
W zasadzie, można pokusić się również o stworzenie skryptu, który edytuje pliki .pov i zmienia linijki z ustawieniami kamery. Zmiana w pliku .pov, którą uzyskałem:
camera {
    right <-image_width/image_height, 0, 0>      // vpython uses right-handed coord. system
    location <0.000000, 0.000000, 173.205081>
    up <0.000000, 1.000000, 0.000000>
    look_at <0.000000, 0.000000, 0.000000>
    angle 60.000000
}
Przerobienie plików z formatu .pov na .png:
for i in *.pov; do povray Height=400 Width=400 Display=false Output_File_Type=N $i 2>/dev/null; done
Nowa wersja skryptu ([1], [2]) do tworzenia animacji video/gif. Wyeliminowałem straty, które pojawiały się podczas składaniu video z plików .png, przez zastosowania kodeka H.264 i manipulacji "Constant Rate Factor" (uwaga, z ustawieniami CRF trzeba ostrożnie)
#!/bin/sh

PALETTE_FILE="tmp_pallete.png"
VIDEO_FILE="output_video.mp4"
INPUT_FILES="img-%4d.png"
OUTPUT_FILE="output.gif"
FILTERS="fps=25"

ffmpeg -r 100 -i $INPUT_FILES -c:v libx264 -crf 0 -preset veryslow $VIDEO_FILE
ffmpeg -v warning -i $VIDEO_FILE -vf "$FILTERS,palettegen" -y $PALETTE_FILE
ffmpeg -v warning -i $VIDEO_FILE -i $PALETTE_FILE -lavfi "$FILTERS [x]; [x][1:v] paletteuse" -y $OUTPUT_FILE
Efekt końcowy:

17 kwietnia 2017

[VPython] Kolizja dwóch ciał z obrotem

Trochę więcej kodu, bo sama detekcja kolizji jest bardziej skomplikowana (szczególnie dla większej ilości krawędzi).

25 marca 2017

[python] biblioteka curses

Zawsze chciałem się nauczyć popularnych bibliotek curses/ncurses, okazuje się, że python posiada w bibliotece standardowej pewien port tej pierwszej. Największym problemem, jaki do tej pory znalazłem jest brak obsługi unicode. Nie wiem, czy nie pożałuje w przyszłości, ale przykład będzie trochę dłuższy - błyskawica.
Podsumowanie najważniejszych funkcji:
  • initscr() - inicjalizacja curses
  • start_color() - należy wywołać zaraz po initscr(), jeżeli chcemy używać kolorów
  • use_default_colors() - mówi aplikacji, aby używała kolorów z terminala.
  • halfdelay() - ile części sekundy aplikacja będzie czekała na naciśnięcie klawisz, po wywołaniu getch()
  • noecho() - nie drukuje wciśniętych klawiszy/znaków na terminalu
  • curs_set() - wyłącza pokazywanie kursora
  • init_color() - tworzy kolor, pierwszy parametr to jego identyfikator, dalsze parametry to wartości RGB, ale podawane w zakresie od 0 do 1000 każda.
  • init_pair() - tworzy parę: identyfikator koloru znaku i koloru tła. Identyfikator -1 oznacza domyślny kolor z terminala (przeźroczysty).
  • addstr() - drukuje string pod wskazanymi współrzędnymi (y, x). Ostatnim parametrem są atrybuty jakie można nadać tekstowi (np. kolor czcionki i tła). Tekst nie zostanie od razu wyświetlony, aby tak się stało konieczne jest zawołanie funkcji refresh()
  • refresh() - odrysowuje ekran
  • clear() - czyści ekran
  • endwin() - przywraca terminal do oryginalnych ustawień
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import curses
import random
from time import sleep
import os
import subprocess
import collections


Light = collections.namedtuple('Light', ['y', 'x', 'symbol'])


class LightningIndex:
    def __init__(self, index, branch):
        self.index = index
        self.branch = branch


def createLightning():
    x = curses.COLS / 2 + random.randint(-10, 10)
    y = 0
    lightning = [Light(y, x, random.choice('/|\\'))]
    branches = []
    while y < curses.LINES - 1:
        _, _, prev_symbol = lightning[-1]
        if prev_symbol == '|':
            y += 1
            symbol = random.choice('/|\\')
        elif prev_symbol == '/':
            symbol = random.choice('/|\\_')
            if symbol == '/' or symbol == '_':
                x -= 1
            if symbol != '_':
                y += 1
        elif prev_symbol == '\\':
            symbol = random.choice('/|\\_')
            if symbol == '\\' or symbol == '_':
                x += 1
            if symbol != '_':
                y += 1
        elif prev_symbol == '_':
            if lightning[-1].x < lightning[-2].x:
                symbol = random.choice('/_')
                x -= 1
            else:
                symbol = random.choice('\\_')
                x += 1
            if symbol != '_':
                y += 1

        if random.randint(0, 30) == 1:
            branches.append(createBranch(lightning[-1], Light(y, x, symbol)))

        lightning.append(Light(y, x, symbol))

    return lightning, branches


def createBranch(prev, root):
    branch = [prev, root]
    y = root.y
    x = root.x
    for i in range(random.randint(15, 30)):
        _, _, prev_symbol = branch[-1]
        if prev_symbol == '|':
            y += 1
            symbol = random.choice('/\\')
        elif prev_symbol == '/':
            symbol = random.choice('/___')
            if symbol == '/' or symbol == '_':
                x -= 1
            if symbol != '_':
                y += 1
        elif prev_symbol == '\\':
            symbol = random.choice('\\___')
            if symbol == '\\' or symbol == '_':
                x += 1
            if symbol != '_':
                y += 1
        elif prev_symbol == '_':
            if branch[-1].x < branch[-2].x:
                symbol = random.choice('/___')
                x -= 1
            else:
                symbol = random.choice('\\___')
                x += 1
            if symbol != '_':
                y += 1

        if x < 0 or x >= curses.COLS or y < 0 or y >= curses.LINES:
            break
        branch.append(Light(y, x, symbol))

    del branch[0]
    return branch


def blink(lightning, attr1, attr2):
    for l in lightning:
        scr.addstr(l.y, l.x, l.symbol, attr1)

    sleep(0.1)
    scr.refresh()

    for l in lightning:
        scr.addstr(l.y, l.x, l.symbol, attr2)

    sleep(0.1)
    scr.refresh()


def indexer(light, branches):
    res = []
    for bs in branches:
        if light.x == bs[0].x and light.y == bs[0].y:
            res.append(LightningIndex(0, bs))

    return res


scr = curses.initscr()
curses.start_color()        # Potrzebne do definiowania kolorów
curses.use_default_colors() # Używaj kolorów terminala
curses.halfdelay(5)         # Ile częśći sekundy czekamy na klawisz, od 1 do 255
curses.noecho()             # Nie drukuje znaków na wejściu
curses.curs_set(False)      # Wyłącza pokazywanie kursora

GRAY = 2
curses.init_color(1, 600, 600, 600)     # Zdefinuj kolor pod identyfikatorem 1,
                                        # daje kolor RGB, ale wartości 0-1000
curses.init_pair(GRAY, 1, -1)           # Stwórz parę tło/czcionka. -1 przeźroczyste

WHITE = 3
curses.init_pair(WHITE, curses.COLOR_WHITE, -1)

random.seed(4876)

while True:
    ch = scr.getch()        # Oczekiwanie aż upłynie czas, lub albo zostanie
                            # naciśnięty klawisz
    scr.clear()             # Czyści ekran

    if ch == ord('q'):
        break

    lightning, branches = createLightning()
    indexed = [LightningIndex(0, lightning)]

    for l in lightning:
        indexed += indexer(l, branches)

        for i in indexed:
            if i.index >= len(i.branch):
                continue

            light = i.branch[i.index]
            scr.addstr(light.y, light.x, light.symbol, curses.color_pair(GRAY))
            i.index += 1

        sleep(0.01)
        scr.refresh()       # Odświeżanie ekranu

    blink(lightning, curses.A_BOLD | curses.color_pair(WHITE),
        curses.A_NORMAL | curses.color_pair(WHITE))
    blink(lightning, curses.A_BOLD | curses.color_pair(WHITE),
        curses.A_NORMAL | curses.color_pair(WHITE))

curses.endwin()             # Przywraca terminal do oryginalnych ustawień
Linki:

18 listopada 2016

[VPython] Siła sprężystości

Dwie symulacje, właściwie bliźniacze, w których prezentowane jest działanie siły sprężystości (bez tłumienia). Każdy układ ma inną stałą sprężystości i położenie początkowe, dlatego zachowują się inaczej. W pierwszym przykładzie na układ działa dodatkowo siła grawitacji


W drugim przykładzie mamy ciało poruszające się na sprężynie horyzontalnie bez poślizgu. Nie jest rozważana siła grawitacja, która i tak by była równoważona przez oddziaływanie podłoża.

1 listopada 2016

paramiko/rforward.py i py2exe

Dobrze jest mieć w zanadrzu mechanizm pozwalający na uruchomienie pythonowego skryptu na systemie pozbawiony interpretera. Do celów testowych wybrałem rforward.py, który pozwala na stworzenie tunelowanego połączenia, pochodzący z biblioteki Paramiko. Wydaje się, że biblioteka jest całkiem rozbudowana, więc może być dobrym polem do testów. Zestaw linków:
Instalacja pakietów dla Paramiko i py2exe:
c:\Python34\Scripts>pip install cryptography
c:\Python34\Scripts>pip install pycrypto
c:\Python34\Scripts>pip install paramiko

c:\Python34\Scripts>pip install py2exe
Domyślny pip dostarczony wraz z interpreterem miał kłopot, aby zainstalować niektóre pakiety. Okazało się, że aktualizacja do najnowszej wersji rozwiązuje problem.
You are using pip version 7.1.2, however version 8.1.2 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.
Problemem okazała się również wersja pythona, której używałem. py2exe działa do wersji 3.4. Wersji 3.5 towarzyszą jakieś zawirowania i trzeba będzie na nią jeszcze poczekać.
Building 'dist\rforward.exe'.
error: [Errno 2] No such file or directory: 'c:\\Python35\\lib\\site-packages\\py2exe\\run-py3.5-win-amd64.exe'
Aby stworzyć plik wykonywalny należy stworzyć skrypt setup.py, w którym wyszczególnimy z jakich plików składa się nasz program.
from distutils.core import setup
import py2exe
from glob import glob

setup(console=['rforward.py'],
      data_files = [('', ['C:\\Windows\\System32\\msvcr100.dll',
                          'C:\\Windows\\System32\\msvcp100.dll'])])
To czego może zabraknąć na docelowej maszynie, to biblioteki z Visual Studio 2010, których wymaga do działania python. Można je również zainstalować za pomocą Microsoft Visual C++ 2010 Redistributable Package (x86). Ja wybrałem wersję na 32-bitową, zgodną z zainstalowanym interpreterem. Tutorial py2exe (dla starszej wersji pythona), ostrzega o restrykcjach odnośnie kopiowania tych bibliotek, a mianowicie powinno kopiować się wszystkie trzy, a nie tylko wybraną oraz manifest. Pozostawię to jako kwestię otwartą, ponieważ, nie mogłem znaleźć tego w dokumentacji, ponadto jeden z plików (msvcp100.dll), gdzieś wyparował. Z tego co wiem do działania wymagany jest jedynie msvcr100.dll (nie testowałem). Pliki należy skopiować, do głównego katalogu, tak, by exe mógł je odnaleźć.
Aby zbudować plik wykonywalny wystarczy wywołać setup.py z opcją py2exe. Całość potrzebna do działania programu powinna znaleźć się w katalogu dist.
c:\Python34\python.exe setup.py py2exe

20 października 2016

[python] Prosty skrypt z uruchomieniem wątku.

Jak mówi Internet CPython w związku z istnieniem mechanizmu Global Interpreter Lock (GIL) nie pozwala na wykonanie więcej niż jednego wątku:
CPython implementation detail: In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing or concurrent.futures.ProcessPoolExecutor. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously.
Niemniej jednak Python posiada stosowne API, które da się wykorzystać w innym interpreterze bez ograniczeń GIL, albo przy zadaniach typu I/O.

Queue (kolejka) zapewnia bezpieczny mechanizm do wymiany informacji pomiędzy wątkami.
  • Każde zawołanie Queue.put() zwiększa licznik, który jest dekrementowany w momencie zawołania Queue.task_done().
  • Główny wątek jest blokowany przez Queue.join(), do momentu, gdy wszystkie elementy z kolejki zostaną przetworzone.
  • Funkcja Queue.get() jest domyślnie blokująca, tzn. wątek zawiesza działania, jeżeli nie ma już danych do przetworzenia.
  • Thread.daemon - wszystkie wątki potomne zostaną zabite wraz z wątkiem głównym.
Często po wywołaniu programu na konsoli pojawia się informacja o wystąpieniu wyjątku. Jest to związane ustawieniem daemon=true. Interpreter nie zdążył z czystym zamknięciem wątku gdy kończony jest wątek główny. Rozwiązaniem byłby mechanizm w samum wątku, pozwalający na jego zakończenie, gdy nie jest on już potrzebny. W przypadku prostych skryptów, raczej nie trzeba się tym przejmować.
# -*- coding: utf-8 -*-

from Queue import Queue
from threading import Thread, current_thread

def worker():
    while True:
        item = q.get()
        print current_thread().name + ': ' + str(item[0] + item[1])
        q.task_done()

q = Queue()
num_worker_threads = 3

for i in range(num_worker_threads):
    t = Thread(target=worker)
    t.daemon = True
    t.start()

for item in [(1, 2), (4, 7), (5, 2)]:
    q.put(item)

q.join()
Wynik:
Thread-2: 3
Thread-2: 11
Thread-3: 7

23 lipca 2016

gdb + python

Bardzo łagodne wprowadzenie do tematyki użycia pythona w gdb i jeszcze raz na podstawie CppCon 2015: Greg Law "Give me 15 minutes & I'll change your view of GDB"
Na początku jednak kilka komend, które okazały się całkiem pomoce w procesie nauki:
  • start - ustawia się na pierwszej instrukcji naszego programu, w zależności od systemu punkt wejścia może mieć różną nazwę, gdb w takim przypadku zachowa się uniwersalnie.
  • save breakpoints mybbb - zapisanie breakpointów do pliku. Przydatne, gdy trzeba zamknąć gdb, ale nie chcemy niszczyć dorobku naszej sesji.
  • source mybbb - załadowanie breakpointów z pliku
Proste wykorzystanie pythona, do odczytania process id. Aby zakończyć interaktywny tryb należy wpisać end, podobnie jak to się dzieje w przypadku command.
(gdb) python
>import os
>print('pid %d' % os.getpid())
>end
pid 17865
Odczyt informacji o breakpointach:
Reading symbols from a.out...done.
(gdb) b 8
Breakpoint 1 at 0x400589: file main.cpp, line 8.
(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400589 in main() at main.cpp:8
(gdb) r
Starting program: /home/user/cpp_gdb_test/a.out 
is i now 7

Breakpoint 1, main () at main.cpp:8
8     i++;
(gdb) python
>print(gdb.breakpoints()[0].location)
>end
/home/user/cpp_gdb_test/main.cpp:8
Ustawienie braekpointa z poziomu pythona:
(gdb) python gdb.Breakpoint('11')
Breakpoint 2 at 0x4005a1: file main.cpp, line 11.
(gdb) c
Continuing.
is i now 8

Breakpoint 2, main () at main.cpp:11
11     printf("Hello world!!!\n");

26 czerwca 2016

Rachunek wektorowy i książki

Kilka przydatnych książek, które znalazłem na wikibooks, ucząc się fizyki. Niektórym brakuje rozdziałów, ale i tak jestem pod wrażeniem. Warto się temu przyjrzeć, jako coś dodatkowego do klasycznych podręczników:
Coś z rachunku wektorowego - wirujący wektor, do którego stworzenia potrzebna jest macierz obrotu [1], [2].
Program koduj macierze przekształceń właściwą dla dwóch wymiarów:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from visual import *
import ImageGrab

display(x=0, y=0, width=400, height=400, userzoom=False,
        center=(0, 0, 1), foreground=(0, 0, 0), background=(1, 1, 1))

ar = arrow(pos=(0, 0, 0), axis=(10, 0, 0), shaftwidth=0.3)

total_angle = 0
angle = 0.1 / (2 * math.pi)
frame = 0
while total_angle <= 2 * math.pi:
    rate(100)

    nx = ar.axis.x * math.cos(angle) - ar.axis.y * math.sin(angle)
    ny = ar.axis.x * math.sin(angle) + ar.axis.y * math.cos(angle)
    nz = 0
    ar.axis = (nx, ny, nz)

    file_name = 'img-' + '{fr:03d}'.format(fr=frame) + '.png'
    frame += 1

    im = ImageGrab.grab((0, 0, 400, 400))
    im.save(file_name)

    total_angle += angle
    print total_angle

exit()

24 czerwca 2016

[python] matplotlib - rysowanie wykresów

Kolejna biblioteka, którą zacząłem się bawić, służąca do rysowania wykresów: matplotlib. Bardzo przyjema dokumentacja, z dużą liczbą przykładów. Dla testu wykreśliłem pochodną i całkę oznaczoną dla funkcji:
$$ f(x) = -2x^{3} - 4x^{2} + 8x + 1 \\
f^\prime(x) = -6x^{2} - 8x + 8 \\
F(x) = \int f(x) dx = -\frac{1}{2}x^{4} - \frac{4}{3}x^{3} + 4x^{2} + x + C \\
F(-3) - F(1) = \int_{-3}^{1} f(x) dx = \left(-\frac{1}{2}x^{4} - \frac{4}{3}x^{3} + 4x^{2} + x + C\right)\Biggr|_{-3}^{1} $$
Kod programu:
#!/usr/bin/env python
# -*- coding: utf-8 -*-


import numpy as np
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt


def main():
    red_patch = mpatches.Patch(color='red', label=r'$f(x)$ - moja funkcja')
    blue_patch = mpatches.Patch(color='blue', label=r'$f^\prime(x)$ - obliczone numerycznie')
    cyan_patch = mpatches.Patch(color='cyan', label=r'$f^\prime(x)$ - obliczone recznie')
    green_patch = mpatches.Patch(color='green', label=r"$\int_{-3}^{1}f(x)$ - obliczone numerycznie")

    plt.legend(handles=[red_patch, blue_patch, cyan_patch, green_patch], loc='lower right')

    x = np.linspace(-4, 2, 10)
    x_dense = np.linspace(-4, 2)

    y_f = func(x)
    plt.plot(x, y_f, 'red', linewidth=2)

    y_deriv = derivative(func, x)
    plt.plot(x, y_deriv, 'blue', linewidth=2)

    y_own_deriv = own_deriv_func(x_dense)
    plt.plot(x_dense, y_own_deriv, 'cyan', linewidth=1)

    summo1 = definite_integral(func, a=-3, b=1)
    summo2 = own_integral_func(a=-3, b=1)
    print 'Całka oznaczona, numerycznie: %f' % summo1
    print 'Całka oznaczona, ręcznie: %f' % summo2

    current_figure = plt.gcf()
    current_figure.savefig('rachunek_rozniczkowy.png')

    plt.show()


def func(x):
    return -2 * (x ** 3) - 4 * (x ** 2) + (8 * x) + 1


def own_deriv_func(x):
    return -6 * (x ** 2) - 8 * x + 8


def own_integral_func(a, b):
    F = lambda x: -(1/2.0) * (x ** 4) - (4/3.0) * (x ** 3) + 4 * (x ** 2) + x
    return F(b) - F(a)


def derivative(fun, x):
    h = 0.2 # dx
    return (fun(x + h) - fun(x)) / h


def definite_integral(fun, a, b):
    axes = plt.gca()
    dx = (b - a) / 20.0

    summo = 0
    x = a
    while x < b:
        axes.add_patch(mpatches.Rectangle(xy=(x, 0), width=dx, height=fun(x), facecolor='green'))
        summo += dx * fun(x)
        x += dx

    return summo


if __name__ == '__main__':
    main()
Wynik:
Całka oznaczona, numerycznie: -26.080000
Całka oznaczona, ręcznie: -25.333333

3 maja 2016

{Machine Learning} Recipes

Google Developers na swoim kanale, rozpoczął serię filmów traktujących o nauczaniu maszynowym. Krótka i ciekawa forma, bardzo mi się podoba.



Odrobinę przerobiony przykład "Hello Wrold"
# http://scikit-learn.org/stable/
# https://www.continuum.io/
# sudo apt-get install python-scikits-learn

from sklearn import tree

# Collect training data
SMOOTH = 1
BUMPY = 0
APPLE = 0
ORANGE = 1

features = [[140, SMOOTH], [130, SMOOTH], [150, BUMPY], [170, BUMPY]]
labels = [APPLE, APPLE, ORANGE, ORANGE]

# Train classifier
clf = tree.DecisionTreeClassifier()
clf = clf.fit(features, labels)

# Make predictions
result = clf.predict([[160, 0]])

if result[0] == APPLE:
    print 'APPLE'
elif result[0] == ORANGE:
    print 'ORANGE'
else:
    'Result unknown'
Wynik:
ORANGE

28 marca 2016

vpython, gify i mechanika klasyczna

Od dawna planuje zabrać się za naukę mechaniki klasycznej. To kolejne podejście, tym razem jednak, zacząłem od poszukiwania narzędzi, które pomogą mi w nauce. Zaczęło się od bardzo fajnego artykułu na temat biblioteki vpython, linki poniżej i inne przydatne:
Za cel eksperymentu postawiłem sobie, wygenerowanie gif-a, z animacją ruchu. Liczyłem, że wystarczy do tego ImageMagic, jednak tworzenie gif-ów jest na tyle zagmatwane, że trzeba było skorzystać również z pakietu FFmpeg. Efekt nie jest do końca satysfakcjonujący. Od pewnego czasu, za bardzo skupiam się nad idealną wersją prototypu. Co więcej staram się znaleźć odpowiedzi na wszystkie pytania jakie się pojawią podczas jego wytwarzania, szczególnie te poboczne. Nie taka był idea tego bloga, czas więc na refleksję.

W każdym razie, oto przerobiony przykład "bouncing ball" z niewielkimi modyfikacjami. Po pierwsze, skorzystałem z biblioteki PIL (Python Image Library), aby tworzyć zrzuty ekranu kolejnych klatek animacji. Biblioteka działa jedynie pod systemem Windows, a jej zamiennik pyscreenshot nie zdał rezultatu. Jakoś się z tym pogodziłem. Druga sprawa, to warunek przerwania animacji, czyli moment gdy piłka znów znajdzie się w tej samej pozycji.
from visual import *
import ImageGrab    # from PIL


starting_height = 4
floor = box (pos=(0, 0, 0), length=4, height=0.5, width=4, color=color.blue)
ball = sphere (pos=(0, starting_height, 0), radius=1, color=color.red)
ball.velocity = vector(0, -1, 0)
dt = 0.01

frame = 0
while 1:
    rate (100)
    ball.pos = ball.pos + ball.velocity*dt
    if ball.y < ball.radius:
        ball.velocity.y = abs(ball.velocity.y)
    else:
        ball.velocity.y = ball.velocity.y - 9.8*dt

    file_name = 'img-' + '{fr:03d}'.format(fr=frame)  + '.png'
    frame += 1

    im = ImageGrab.grab((0, 0, 500, 500))  # screen box from (0,0)-(500,500)
    im.save(file_name)                     # save image to disk

    if ball.pos.y > starting_height:
        exit()
Skrypt wygenerował około 140 zrzutów ekranu, które trzeba było poddać obróbce, nieoceniony okazał się tutaj ImageMagic. Kluczowym problem jest wysterowanie czasu pomiędzy kolejnymi klatkami.
Wszystkie obrazki zawierały ramki Windows-owego okienka, które trzeba było usunąć:
Przycinanie zdjęcia za pomocą mogrify (z ImageMagic). Pozycje i rozmiar dobrałem eksperymentalnie.
mogrify -crop 413x411+9+30 +repage $(ls -v *.png)
Chociaż ImageMagic potrafi generować gif-y, jednak za żadne skarby nie mogłem skonfigurować właściwego opóźnienia, pomiędzy kolejnymi klatkami. Szperając po internecie udało się ten problem ominąć, wymaga to jednak kroków pośrednich, w postaci utworzenia filmiku z animacją, a następnie przepuszczenia go jeszcze raz przez ffmpeg i convert (ImageMagic). Magiczny zestaw komend:
# Filmik ze zdjęć
ffmpeg -r 100 -f image2 -i img-%3d.png video.avi

# Wyciąganie klatek z filmiku i zlepianie w gif-a
ffmpeg -i video.avi -vf scale=320:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - gif:- | convert -layers Optimize - output.gif

# Alternatywna wersja, też działa
ffmpeg -i video.avi -vf scale=320:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - output2.gif
W animacji jest jakiś uszczerbek jakości, tak jakby brakowało wszystkich potrzebnych klatek. Być może uda mi się kiedyś rozwiązać tą zagadką i odpowiednio dobrać parametry, wtedy też o nich napiszę. Próbowałem także innych rozwiązań, bezskutecznie próbując wysterować opóźnienie pomiędzy kolejnymi klatkami.
# Gif generowany tylko przez convert (ImageMagic)
convert -dispose none -delay 1.5 $(ls -v *.png) -coalesce output3.gif

# Gif generowany tylko przez ffmpeg
ffmpeg -f image2 -framerate 100 -i img-%3d.png output4.gif

2 stycznia 2016

[python] collections

Trzy klasy z pakietu collections, których nigdy wcześniej nie korzystałem, a okazały się niezwykle przydatne.
Bywa że do krotki chcemy wstawić na tyle dużo danych, że zaczynamy się gubić w tym, na której pozycji znajduje się to co nas interesuje. Zamiast tworzyć klasę z polami, można skorzystać z namedtuple.
from collections import namedtuple

User = namedtuple('User', ['name', 'address', 'email'])

user = User('Bob', 'blog.example.com', 'bob@example.com')
print('Name:  ' + user.name)
print('Web:   ' + user.address)
print('Email: ' + user.email)
Wynik:
Name:  Bob
Web:   blog.example.com
Email: bob@example.com
Counter pozwala na zliczanie elementów - zamiast dedykowanej pętli for.
from collections import Counter

indexer = Counter(['Bob', 'Alice', 'Bob', 'Bob'])
print(indexer['Bob'])
Wynik:
3
defaultdict jest klasą pozwalają na ustawienie domyślnej wartości, dla nowych elementów. Przydatne, gdy dodajemy coś do wartości i nie chcemy robić testu na to, czy taki element już istnieje.
from collections import defaultdict

d = defaultdict(lambda: '-')
d['Bob'] += 'one_'
d['Alice'] += 'two_'
d['Bob'] += 'three_'

print(d)
Wynik:
defaultdict(<function <lambda> at 0x7fbf33b27578>, {'Bob': '-one_three_', 'Alice': '-two_'})

26 września 2015

Windows Kernel Exploitation - warsztat

Kilka interesujących artykułów, które znalazłem, a dotyczyły one wykorzystanie podatności w jądrze systemu Windows. Punktem wejścia jest tutaj przede wszystkim interakcja z API win32k.sys. Podstawowe funkcje:
  • zawiera windows manager
    • kontroluje wyświetlania okien
    • zarządza wyjściem na ekran
    • kolekcjonuje dane wejściowe z klawiatury, myszy itd.,
    • woła zdefiniowane przez aplikacja hook-i
    • przekazuje mesgi użytkownika do aplikacji
    • zarządza obiektami użytkownika
  • zawiera graphics device interface (GDI) - bibilioteki funkcji graficznych
    • rysowanie i manipulowanie obiektami: liniami, tekstem i figurami
    • zarządza obiektami (GDI) takimi jak brushes, pens, DCs itp.
    • dostarcza API dla sterowników viedeo/drukarki
W sumie po stronie użytkownika można wywołać ponad 800 funkcji. Warto też wiedzieć, że każdy typ obiektu zdefiniowany jest prze unikalną strukturę (np. win32k!tagWND, win32k!tagCURSOR, itp.).

Szczerze mówiąc najbardziej zainteresował mnie warsztat, jakim posługują się autorzy artykułów. Nie kierowałem się żadnym kluczem, po znalezieniu pierwszego artykuły przeskakiwałem do przypisów i tym sposobem stworzyłem sobie małą listę. Nie zagłębiałem się też zbyt mocno w samą tematykę, więc jest to coś do czego będzie warto wrócić.
Ostatnia źródło informacji to bardzo fajna prezentacja z konferencji Blackhat 2011 ("Kernel Attacks Through User- Mode Callbacks") i opowiada o samej architekturze systemu i klasach błędów jakie można w nim napotkać: use-after-free (alokacja i niszczenie obiektów np. znajdujących się w menu), null-pointer-dereference. Więcej około 30-35 minuty.