Posty oznaczone etykietą python

Vim jako IDE dla programisty Python

Tak kiedyś było

Używałem wielu edytorów programisty. Zaczynałem od dostarczanych wraz z językiem programowania (Amos, BlitzBasic), używałem intensywnie CED-a, później Notatnika, przez jakieś Notatniki+. Był też krótki romans z Pajączkiem ze względu na wsparcie HTML, później używałem Quanty i edytora z Midnight Commandera. Kilka lat temu zachwyciło mnie IDE Microsoft Visual Studio (programowałem nieco w .NET/C#, świetna technologia). Szukałem później pocieszenia instalując Eclipse.

Pewnego spokojnego dnia, gdzieś między zaznaczeniem obszaru tekstu myszą i wciśnięciem Ctrl+C i Ctrl+V zwróciłem uwagę, jak Onjin pisze kod. W zasadzie to nie wyglądało jakby pisał, tylko bezpośrednio przelewał myśli w źródła, wskazując jedynie miejsca docelowe opuszkami z prędkością chyba 200 uderzeń na sekundę.

Dziś jest Vim

Poczułem moc Vim-a. Dziś to moje podstawowe narzędzie pracy. Zasadniczo Vim z kolorowaniem składni Pythona i ewentualnie ctags wystarcza. Jednak postanowiłem trochę rozbudować moje IDE:

  1. TagList

    Plugin, który wyświetla strukturę kodu źródłowego.

    Zrzut ekranu z pluginem TagList w akcji

    Instalacja:

    Otwarcie okna z listą: :Tlist

    Podstawowe skróty:

    `p`: skok do definicji klasy/funkcji
    `o`: otwarcie definicji w nowym oknie (split)
    `F1`: pomoc (lista skrótów)
    `/`: standardowe wyszukiwanie
    
  2. TaskList

    Plugin wyświetla listę zadań do wykonania w otwartym pliku. Działa podobnie jak zakładki TO-DO z innych IDE. Odnajduje komentarze z adnotacjami: TODO, FIXME, XXX. Listę tokenów może przekonfigurować (dokładny opis konfiguracji znajduje się w skrypcie).

    Screenshot przedstawiający działanie pluginu TaskList

    Do pobrania: http://www.vim.org/scripts/script.php?script_id=2607

    Instalacja:

    • skopiować tasklist.vim do katalogu plugin (u mnie ~/.vim/plugin/)
  3. Rope - wsparcie refaktoryzacji

    Ciekawie zapowiadający się pakiet napisany w Pythonie wspomagający zadania refaktoryzacji kodu. Jestem na etapie zaznajamiania się z możliwościami pakietu, aczkolwiek muszę przyznać, że zmiana nazw klas czy modułów sprawdza się rewelacyjnie.

    Do Vim-a istnieje adekwatny plugin (wymaga skompilowanego Vim-a z obsługą Pythona):

  4. Pyflakes

    Sprawdzanie poprawności kodu "w locie". Skrypt jest bardzo wydajny, bo uruchamia się podczas wyjścia z trybu insert.

    Pyflakes skutecznie powiadamia o błędach takich, jak:

    • redeklaracja zmiennych, które nie były używane,
    • brak importów,
    • nieużywane importy i zmienne,
    • użycie zmiennej przed jej definicją,
    • podwójne zdefiniowanie funkcji,
    • błędy składni,
    • itp..

    Więcej informacji: http://www.divmod.org/trac/wiki/DivmodPyflakes

    Skrypt Vim: http://www.vim.org/scripts/script.php?script_id=2441

    pyflakes.vim działa tylko z forkiem dostarczanym w paczce. Niestety rozpada się z wersją oficjalną. Jest to dość duży problem.

  5. SnipMate - snippety rodem z TextMate

    Zaawansowany pakiet skryptów ułatwiających pisanie kodu za pomocą tzw. snippetów. Snippety uaktywnia się wpisując odpowiednią frazę i klepiąc tabulator.

    snipMate.vim Introductory Screencast from Michael Sanders on Vimeo.

    Przydatne snippety dla Pythona:

    • docs: docstring dla modułu
    • for: pętla for
    • while: pętla while
    • def: definicja funkcji
    • cl: definicja klasy
    • defs: definicja metody
    • try: różne rodzaje bloku try-except (do wyboru)
    • ifmain: oj, złoty snippet używany do krótkich testów :)
  6. VimPDB - debugger Pythona w Vim

    Używa się analogicznie jak ipdb czy pdb. W kodzie źródłowym umieszczamy wywołanie:

      import vimpdb
      vimpdb.set_trace()
    

    Przed uruchomieniem skryptu do debugu należy odpalić sesję vim`a:

      vim --severname VIMPDB
    

    Interfejs VimPDB nie jest tak wygodny jak winpdb. Ja i tak najczęściej debuguje używając pdb/ipdb.

  7. Omnicompletion

    Znakomicie sprawdza się w przypadku pakietów zainstalowanych w systemie. Omnicompletion to inna nazwa wygodnego mechanizmu podpowiadania "intellisense".

    Skrót Ctrl+X Ctrl+O podpowiada metody, moduły do importu, nazwy klas. Ustawiając set completeopt=preview mamy na bieżąco wgląd w docstring dopełnianego elementu.

    Dla Pythona wystarczy ściągnąć skrypt Pythoncomplete i skopiować go do ~/.vim/autoload.

    W .vimrc ustawiamy:

     autocmd FileType python set omnifunc=pythoncomplete#Complete
     set completeopt=preview,longest,menu
     set completefunc=pythoncomplete#Complete
    

    (Nie)stety Używam buildout i omnicompletion nie działa w pełni z powodu nie ustawionego prawidłowo PYTHONPATH. Na ten czas nie mam rozwiązania tego problemu. Stąd nie używam omnicomplete za często i nie jest mi do życia potrzebne.

  8. Pylint

    Linter dla Pythona. Z jego zalet chyba wystarczy podać najważniejsze:

    • Pylint sprawdza zgodność z PEP,
    • ocenia kod i porównuje wynik z poprzednim.

    Jest trochę upierdliwy, więc nie uruchamiam go automatycznie (można go skonfigurować, aby nie uruchamiał się po każdym zapisie). Najczęściej uruchamiam go przed deploymentem wersji lub przed wysłaniem zmian do publicznego repozytorium.

    Źródła:

    Instalacja:

    • easy_install pylint
    • skrypt pylint.vim skopiować do ~/.vim/compiler/
    • dodać do .vimrc:

      autocmd FileType python compiler pylint

Rady

  1. Katalog ~/.vim dobrze jest wersjonować:

    cd ~/.vim
    git init
    git add .
    git commit -am 'moj dobry config'
    

    Jeśli plugin okaże się nieciekawy lub błędny wystarczy wycofać się do poprzednich rewizji lub po prostu użyć git clean -f.

  2. Przeładowanie Vim-a pluginami może go dość spowolnić.

Czy czegoś więcej potrzeba? Chyba tylko lepszej integracji z projektami opartymi na buildout. Będzie to temat na kolejnego posta.

Czytaj więcej : komentarze (1) : Paź. 20, 2010

PyCon 2010, 8-10 października

Prelekcje:

Więcej informacji na http://pl.pycon.org/2010/agenda

Do zobaczenia na PyCon 2010!

Źródło: http://pl.pycon.org/2010/

Czytaj więcej : komentarze (0) : Wrz. 21, 2010

Particle Swarm Optimization

Ostatnio interesuję się tematyką algorytmów metaheurystycznych. Ze znajomym pracujemy nad implementacją algorytmu zainspirowanego współpracą stadną nietoperzy (Swarm Intelligence, PSO) z użyciem mechanizmu echolokacji.

Drążąc temat znalazłem ciekawy i prosty przykład PSO zaimplementowanego w Pythonie na stronie francuskiego inżyniera Maxime Biais.

Implementacja wzorowana zachowaniami nietoperzy nieco się różni. Jako, że niewiele jest jeszcze opracowań w tym temacie, opiszę te zagadnienia szerzej na blogu w niedługim czasie.

Czytaj więcej : komentarze (0) : Maj 22, 2010

Symfony forms vs Django forms

Mam okazję pracować z obydwoma frameworkami i mogę je porównać w praktyce. Django zacząłem używać jakieś dwa lata temu, a Symfony nieco wcześniej (od wydania stabilnej wersji 1.0).

Ostatnimi czasy, z braku możliwości upgrade Symfony w projekcie, przeportowałem mechanizm formularzy z wersji 1.1 do 1.0.20.

Piersze wrażenie

Mechanika formularzy w Symfony mocno przypomina newforms z Django. Powszechnie wiadomo, że Fabien jest fanem Django, więc nie zdziwiło mnie zbyt specjalnie, że wzorował się właśnie na nim.

Mamy do dyspozycji podstawową klasę formularza sfForm (django.forms.Form), oraz klasę dedykowaną do modelu Propela sfPropelForm (django.forms.ModelForm).

Jest do dyspozycji zbiór widgetów w klasach sfWidgetForm (django.forms.widgets) do renderingu pól oraz zestaw walidatorów w klasach sfValidator (w Django sprawdzanie poprawności danych jest rozwiązane przez wywoływanie metod clean_FIELDNAME() formularza, o ile zostały zdefiniowane, oraz metody clean() każdego pola). W Symfony jest możliwość napisania własnych specyficznych widgetów i walidatorów.

Używanie formularzy jest podobne - w widoku (sf: akcji), najczęściej przy requescie wysłanym POST-em instancjonujemy formularz i wypelniamy go danymi z requestu, sprawdzamy czy jest prawidlowy (sfForm::is_valid()). W przypadku sfPropelForm (ModelForm) po walidacji wywołujemy save().

Definiowanie formularzy

W Django używamy class properties i zagnieżdżonej klasy Meta.

class MyForm(forms.Form):
  name = forms.CharField()
  birthdate = formd.DateField()

  class Meta:
    exclude = ('birthdate',)

W Symfony formularz konfigurowany jest w metodzie sfForm::configure(), która uruchamiana jest domyslnie w konstruktorze.

class MyForm extends sfForm
{
  protected function configure() {
    $this->setWidgets(array(
        'name' => new sfWidgetFormInput(array('required'=>true,)),
        'birthdate' => new sfWidgetFormDate(array('required'=>true,)),
        ));
  }
}

Ops, ale.. jak wykluczyć datę urodzin?

Google dają odpowiedź:

W Symfony zwykle kończy się to na klasie pochodnej, przeciążeniu configure() z serią unset na widgetSchema i validators. Możliwe jest (wszystko jest możlwie do realizacji, ale z różną efektywnością i poziomem trudności), ale trochę uciążliwe.

Django-Symfony 1:0

Uważny czytelnik zwróci uwagę, że w Symfony nie definiujemy pół formularza (sic!) tylko oddzielnie widgety i validatory. Mimo, że istnieje klasa sfFormField, jest ona używana już po bindingu (zawiera w sobie wartość pola - w Django jest BoundField, które opakowuje Field i dostarcza wartość).

Jaka jest zatem kolejna wada formularzy Symfony? Nie ma zdefiniowanych klas pól formularzy, które naturalnie łączą widgety i walidaję danych, np. CharField związane jest domyślnie z widgetem TextInput i posiada domyślną walidację w metodzie CharField.clean(). W Symfony trzeba dodać walidację samodzielnie:

class MyForm extends sfForm
{
  protected function configure() {
    $this->setWidgets(array(
        'name' => new sfWidgetFormInput(array('required'=>true,)),
        'birthdate' => new sfWidgetFormDate(array('required'=>true,)),
        ));

    $this->setValidators(array(
        'name' => new sfValidatorString(array('required'=>true)),
        ));
  }
}

Walidacja pola jest zwykle specyficzna. W Symfony trzeba napisać dedykowaną klasę walidatora. W Django implementujemy metodę clean_FIELDNAME w klasie formularza. Można też przygotować dedykowaną klasę Field.

Django-Symfony 2:0

I18n (czyli formularze po polsku)

W Django etykiety i komunikaty walidacji muszą być opakowane w wywołanie funkcji gettext. W komunikatach podstawowych pól używany jest ugettext_lazy. Etykiety musimy zdefiniować ręcznie (za pomocą argumentu label przy definiowaniu pola formularza).

class MyForm(forms.Form):
  name = forms.CharField(label=_('Name'))
  birthdate = formd.DateField(label=_('Birth date'))

  class Meta:
    exclude = ('birthdate',)

Po stronie Symfony zrealizowano to nieco inaczej - przez nastawienie funkcji callbacka do funkcji translate:

  protected function configure() {
      $this->widgetSchema->getFormFormatter()->setTranslationCallable(
          array(sfContext::getInstance()->getI18N(), '__'));
      /* ... */
  }

Pełny kod formularza w Symfony:

class MyForm extends sfForm
{
  protected function configure() {

    $this->widgetSchema->setNameFormat('my_form[%s]');
    $this->widgetSchema->getFormFormatter()->setTranslationCallable(
        array(sfContext::getInstance()->getI18N(), '__'));

    $this->setWidgets(array(
        'name' => new sfWidgetFormInput(array('required'=>true,)),
        'birthdate' => new sfWidgetFormDate(array('required'=>true,)),
        ));

    $this->setValidators(array(
        'name' => new sfValidatorString(array('required'=>true)),
        'birthdate' => new sfValidatorDate(array('required'=>true)),
        ));
  }
}

Pamiętacie jeszcze, że w Django ten sam opis formularza składa się tylko 5 linii kodu? :)

Mimo innego sposobu używania gettext(), to zarówno Django, jak i w Symfony dają radę. Remis.

Django-Symfony 3:1

Domyślne dane (initial parameters)

Django:

  data = {'name': 'Stranger',}
  form = MyForm(initial=data)

Symfony:

  $form = new MyForm();
  $form->setDefaults(array('name'=>'Stranger'));

Praktycznie to samo. Jednak zauważyłem, że pomimo nastawienia wartości domyślnych w formularzu sfPropelForm, pusta wartość pola modelu napisuje to, co przekazujemy w defaults. Jest to dość duży problem, ale może dotyczyć tylko wersji formularzy z Symfony 1.1. Remis.

Django-Symfony 4:2

Binding

Formularze Django w konstruktorze przyjmują parametr data, do którego zwykle przekazuje się request.POST. Analogicznie jest z przesłanymi plikami - request.FILES. Przykład:

def my_form(request):
  if request.method == 'POST':
      form = MyForm(request.POST, request.FILES)
      if form.is_valid():
          model = form.save()
          return redirect(model)

W Symfony jest dość podobnie, aczkolwiek binding należy wykonać oddzielną metodą bind():

public function executeMy_form() {
  $form = new MyForm();
  if($this->getRequest()->getMethod() == sfWebRequest::POST) {
      $form->bind($this->getRequestParameter('my_form'),
        $this->getRequest()->getFiles('my_form'));
      if($form->isValid()) {
          $form->save();
          $this->redirect('/somewhere/');
      }
   }
}

Należy też pamiętać o użyciu prawidłowej zmiennej z requestu - w klasie formularza definiujemy name format (setNameFormat) i te obydwie części muszą się zgadzać. W ten sposób złamano DRY i nie jestem, czy w Symfony da się ten problem jakoś ominąć. Remis.

Django-Symfony 5:3

Częste problemy/tips

Fantazje Fabiena

Na koniec, jako ciekawostkę, dodam krótkie podsumowanie tego, co Fabien zaimplementował będąc albo przemęczonym, albo na kacu ;)

Podsumowanie

Formularze w Symfony są, a to już lepsze niż nic, które było w wersji 1.0.X. Ostateczny wynik rozgrywki Django-Symfony 5:3. Jeśli ktoś nie używał formularzy w Symfony, to gorąco polecam. Jeśli ktoś ma dylemat co wybrać, polecam Django z racji szerszych możliwości i skrócenia czasu realizacji zadań.

Czytaj więcej : komentarze (0) : Kwiecień 28, 2010

Zmiana silnika bloga

Nadszedł ten czas. Rezygnuję z Bloggera na rzecz własnej strony domowej, dla realizacji której Blogger nie wystarcza.

Postanowiłem użyć aplikacji django-simpler-blog i rozszerzyć ją o brakujące funkcjonalności. Dzięki temu zabiegowi projekt mojej strony domowej jest łatwiejszy do realizacji, a swoją drogą mam wpływ na każdy detal. Ponad to pisanie w edytorze WYSIWYG lub bezopośrednio HTML nie jest sympatyczne. Wolę zdecydowanie Markdown.

Do realizacji tego bloga użyłem również:

Czytaj więcej : komentarze (0) : Lut. 16, 2010