Mostrando postagens com marcador Python. Mostrar todas as postagens
Mostrando postagens com marcador Python. Mostrar todas as postagens

sábado, 29 de outubro de 2011

Django: Quantidade de queries executada por teste

Django-Dynamic-Fixture 1.4.0 possui bugfixes e novas funcionalidades:

- Relatório com o número de queries executadas em cada método de teste: manage.py test --with-queries
- Conta quantas queries são executas ao inserir e atualizar um objeto (pode variar de acordo com listeners, sobrescrita do método save, etc): manage.py count_queries_on_save

http://code.google.com/p/django-dynamic-fixture/

quarta-feira, 12 de outubro de 2011

Apresentação sobre testes na Python Brasil 2011

Padrões de Testes Automatizados com Django

http://www.slideshare.net/paulocheque/padroes-de-testes-automatizados-com-django

domingo, 9 de outubro de 2011

Feature Flip para Continuous Deployment? Django Intruder

As ferramentas mais populares para ativar e desativar funcionalidades no Django são intrusivas, ou seja, é necessário acrescentar código adicional para permitir que uma funcionalidade seja ativada ou desativada. Essa abordagem é interessante, mas complexa para resolver um problema simples, além do que não é uma solução 100% dinâmica.

Django Intruder é uma solução simples e transparente para a aplicação. Sem qualquer código adicional é possível desativar qualquer request ao sistema.

http://code.google.com/p/django-intruder/

http://pypi.python.org/pypi/django-intruder

terça-feira, 15 de março de 2011

Boas práticas com Django

[Editado em 2011/03/27]: melhor explicado e detalhado o primeiro tópico.
 Depois dos tópicos de antipadrões com Django (http://pythonsmalltalk.blogspot.com/2011/03/discussao-sobre-django-internet-e.html), agora um tópico de boas práticas.
  • Usar filter e exclude somente dentro de managers ou models: Com django é tão fácil fazer query que acabamos inserindo filters e excludes em tudo quanto é local, tais como views e forms, o que é uma ideia terrível. Prejudica a testabilidade do código, mistura responsabilidades dos objetos etc.
  • Dar nomes às urls e usar reverse e a tag url: Usar urls hard-coded além de prejudicar a manutenção, ainda dificulta i18n. Além disso, a aplicação pode deixar de ser um "pluggable", ou seja, dificilmente poderá ser reaproveitada em outros projetos.
  • Utilizar select_related sempre que possível/necessário para otimização (http://docs.djangoproject.com/en/1.2/ref/models/querysets/#select-related)
  • Usar distinct em buscas que envolvem várias tabelas. Mais comumente quando são usados campos many to many para buscas. Isso é pra evitar que uma busca traga registros repetidos. Chatices de SQL que são propagadas nos resultados das buscas. (http://docs.djangoproject.com/en/1.2/ref/models/querysets/#distinct)
  • Criar Custom Fields caso um field siga um padrão (http://docs.djangoproject.com/en/1.2/howto/custom-model-fields)
  • Sempre dar redirect depois de salvar um formulário (básico de qualquer framework Web).
  • Não utilizar o arquivo tests.py. Criar uma pasta tests e adicionar arquivos de teste para arquivo da aplicação.
  • Há várias maneiras de organizar o settings. Uma básica e boa é criar (pelo menos) um arquivo de settings para desenvolvimento e um para produção.

domingo, 13 de março de 2011

Django Dynamic Fixture 1.2.1


Lançada nova versão da ferramenta (1.2.1). A documentação foi atualizada, incluindo comparação com outras ferramentas.

Antes dessa versão, a ferramenta atrapalhava a criação de objetos que tinham campos auto calculados, como é o caso dos objetos que usam a ferramenta Dango-MPTT (http://code.google.com/p/django-mptt).

Django Dynamic Fixture 1.2.1http://code.google.com/p/django-dynamic-fixture

quarta-feira, 9 de março de 2011

Discussão sobre Django, Internet e frameworks Web

Já mexi com muitos frameworks Web, de diferentes linguagens: JSP/Servlets, Struts, JSF, GWT, Echo2 (todos anteriores para Java), Django (Python), Grails (Groovy), Rails (Ruby) etc. Desses todos, destaco dois como sendo os mais inteligentes: Django e GWT.

Django porque é muito simples, legível e poderoso.

GWT porque foi criado pensando completamente em comandos assíncronos. Talvez apenas o Echo2 poderia se comparar a ele.

Ainda não existe um framework perfeito (ou quase), até mesmo porque a internet ainda é muito bagunçada. Prova disso são as próprias "linguagens" de HTML, Javascript e CSS que são completamente rebuscadas. Não é a toa que hoje existem diversas ferramentas para geração de CSS e ferramentas de Javascript, tais como JQuery, que não apenas criam funções bacanas para serem usadas, mas que também fazem uma série de correções e adaptações para navegadores e pro próprio HTML/Javascript para que o desenvolvimento Web se torne menos amaldiçoado.

Isso dá até um bom argumento para a Microsoft e o IE não adotarem certos padrões e convenções. Uma empresa do tamanho da Microsoft não deve acatar a padrões mal definidos. Claro que não estou defendendo a Microsoft, até porque isso só piora ainda mais a situação. Entretanto, é compreensível.

Mas enfim, onde quero chegar é que Django não é perfeito e, acredito, tem certas coisas que devem ser evitadas. Segue uma lista:

  • Framework de permissões: Acho útil apenas para interface de Admin. Para uso interno, não faz sentido. Devemos simplificar as coisas, não complicar. Logo, devemos trabalhar com grupos de usuários (vide django snippets). Perdemos em flexibilidade mas ganhamos em simplicidade e clareza.
  • Formsets, Inline Formsets etc: Foram criados pensando na interface de Admin. Apesar de quebrarem um galhão as vezes, são muito rebuscados e limitados.
  • Templates para comandos assíncronos: Templates no Django são muito legais, principalmente seu sistema de herança que é muito simples. Contudo, meu modo de ver é que templates é uma arquitetura pensando em páginas e comandos síncronos. Quando vamos trabalhar com chamadas assíncronas, eles perdem importância. Django não foi arquiteturado como o GWT, logo, não é trivial criar componentes de interface de usuário.
  • get_absolute_url: O quê tem a ver url com a base de dados? Isso é uma tentativa de DRY, mas, ao meu ver, equivocada. Primeiro porque deixa confuso aonde a url será definida (temos o urls.py pra isso). Depois porque mistura de responsabilidade da camada de dados/negócios com a de exibição.
  • Fixtures (estáticas) nos testes: Fixtures estáticas, não importa se é xml, yaml, json etc, são horríveis para manutenção. O máximo é ter um initial_data com usuários, grupos, permissões e algum outro dado básico e olhe lá.
À medida que for tendo novos pensamentos vou adicionando aqui.

Mockito-Python

A biblioteca mais legal de mocks para Java é a Mockito (http://mockito.org), que na verdade é uma biblioteca de spies e stubs, não apenas de mocks. Segunda mais simples e objetiva a EasyMock e, por último, JMock.

Para python não conhecia nenhuma biblioteca de objetos dublês bacana (simples e clara), até que vi que em 2010 surgiu a Mockito-Python:
http://code.google.com/p/mockito-python/downloads/list

Um link bacana de comparação entre outras ferramentas:
http://code.google.com/p/pymox/wiki/MoxComparison

domingo, 27 de fevereiro de 2011

Django Dynamic Fixture

Utilizar dados estáticos para testes é um antipadrão. Mesmo assim, o Django fornece facilidades para usar fixtures estáticas (yaml, json...) nos testes. Não use! A manutenção dos testes se torna péssima.

A melhor solução é criar os objetos dinâmicamente para cada caso de teste. Para facilitar, pode-se criar fixtures que criam instância de objetos já com dados populados. Melhor ainda, deixe que uma ferramenta faça isso por você, como é o caso da Django Dynamic Fixture: http://code.google.com/p/django-dynamic-fixture/

Instalação: pip install django-dynamic-fixture
Documentação: A documentação está toda na página inicial.

quinta-feira, 9 de setembro de 2010

Refactory my code

Um site muito bacana para buscar código fonte, soluções etc para várias linguagens:

http://refactormycode.com/codes/recent/python

Alias, muitas das minhas buscas no google por soluções, bugs etc acabam caindo no site StackOverflow. Também é um ótimo site: http://stackoverflow.com

terça-feira, 7 de setembro de 2010

Combinação de Listas em Python

Combinação de Listas: Imagina que você tem diversas listas e você quer montar todas as combinações de um número específico de elementos de cada lista.

Implementação:

class CombinationOfListsGenerator(object): def __init__(self, lists, amount_per_list): if(amount_per_list == None or lists == None): raise Exception('Invalid argument: None.') elif len(lists) < 1: raise Exception('Invalid argument: Empty list.') elif not isinstance(amount_per_list, int): if len(lists) > len(amount_per_list): raise Exception('Invalid argument: It is missing information in amount_per_list, too few amounts.') else: for l, a in zip(lists, amount_per_list): if len(l) < a: raise Exception('Invalid argument: Amount per list is bigger than size of the list.') self.lists = lists self._current = [] self.generators = [] if isinstance(amount_per_list, int): for l in self.lists: generator = CombinationGenerator(len(l), amount_per_list) self.generators.append(generator) else: for l, a in zip(self.lists, amount_per_list): generator = CombinationGenerator(len(l), a) self.generators.append(generator) self._total = 1 for generator in self.generators: self._total *= generator.total() self.reset() def total(self): return self._total def reset(self): self._current = [] for generator in self.generators[:-1]: generator.reset() generator.next() # last one is loaded in 'next' method self.generators[-1].reset() def current(self): return self._current def has_next(self): return not (not self.generators[0].has_next() and not self.generators[-1].has_next()) def _combination_of_list(self, list, indexes): combination = [] for index in indexes: combination.append(list[index]) return combination def _update_generators(self): # Atualiza anteriores i = len(self.generators) - 1 while i >= 0 and not self.generators[i].has_next(): self.generators[i].reset() self.generators[i].next() i -= 1 if i < 0: # Tratando fim: i = 0 self.generators[i].next() def _update_current(self): self._current = [] for i in range(0, len(self.generators)): indexes = self.generators[i].current() self._current.extend(self._combination_of_list(self.lists[i], indexes)) def next(self): self._update_generators() self._update_current() return self._current
Testes de Unidade:

class CombinationOfListsTests(unittest.TestCase): def test_input_accepts_int_or_list(self): CombinationOfListsGenerator([[1], [2], [3]], 1) CombinationOfListsGenerator([[1], [2], [3]], [1,1,1]) def test_invalid_input(self): self.assertRaises(Exception, CombinationOfListsGenerator, None, 1) self.assertRaises(Exception, CombinationOfListsGenerator, [1], None) self.assertRaises(Exception, CombinationOfListsGenerator, [], 1) self.assertRaises(Exception, CombinationOfListsGenerator, [[1],[2]], 2) self.assertRaises(Exception, CombinationOfListsGenerator, [[1]], 0) # len([1]) < len([[1],[2]]) self.assertRaises(Exception, CombinationOfListsGenerator, [[1],[2]], [1]) self.assertRaises(Exception, CombinationOfListsGenerator, [[1,2],[2],[3]], [2,1]) # 2 > len([2]) self.assertRaises(Exception, CombinationOfListsGenerator, [[1],[2]], [1, 2]) self.assertRaises(Exception, CombinationOfListsGenerator, [[1],[2,3]], [1, 3]) def test_1_element_of_each_list_of_size_1(self): generator = CombinationOfListsGenerator([[1], [2]], 1) self.assertEquals(1, generator.total()) self.assertEquals([1, 2], generator.next()) def test_1_element_of_each_list_of_size_2(self): generator = CombinationOfListsGenerator([[1, 2], [3, 4]], 1) self.assertEquals(4, generator.total()) self.assertEquals([1, 3], generator.next()) self.assertEquals([1, 4], generator.next()) self.assertEquals([2, 3], generator.next()) self.assertEquals([2, 4], generator.next()) def test_2_elements_of_each_list_of_size_2(self): generator = CombinationOfListsGenerator([[1, 2], [3, 4]], 2) self.assertEquals(1, generator.total()) self.assertEquals([1, 2, 3, 4], generator.next()) def test_2_elements_of_each_list_of_size_3(self): generator = CombinationOfListsGenerator([[1, 2, 3], [4, 5, 6]], 2) self.assertEquals(9, generator.total()) self.assertEquals([1, 2, 4, 5], generator.next()) self.assertEquals([1, 2, 4, 6], generator.next()) self.assertEquals([1, 2, 5, 6], generator.next()) self.assertEquals([1, 3, 4, 5], generator.next()) self.assertEquals([1, 3, 4, 6], generator.next()) self.assertEquals([1, 3, 5, 6], generator.next()) self.assertEquals([2, 3, 4, 5], generator.next()) self.assertEquals([2, 3, 4, 6], generator.next()) self.assertEquals([2, 3, 5, 6], generator.next()) def test_1_element_of_each_list_of_size_2(self): generator = CombinationOfListsGenerator([[1, 2], [3, 4], [5, 6]], 1) self.assertEquals(8, generator.total()) self.assertEquals([1, 3, 5], generator.next()) self.assertEquals([1, 3, 6], generator.next()) self.assertEquals([1, 4, 5], generator.next()) self.assertEquals([1, 4, 6], generator.next()) self.assertEquals([2, 3, 5], generator.next()) self.assertEquals([2, 3, 6], generator.next()) self.assertEquals([2, 4, 5], generator.next()) self.assertEquals([2, 4, 6], generator.next()) def test_custom_elements_of_each_list_of_size_2(self): generator = CombinationOfListsGenerator([[1, 2], [3, 4], [5, 6]], [1, 2, 1]) self.assertEquals(4, generator.total()) self.assertEquals([1, 3, 4, 5], generator.next()) self.assertEquals([1, 3, 4, 6], generator.next()) self.assertEquals([2, 3, 4, 5], generator.next()) self.assertEquals([2, 3, 4, 6], generator.next()) def test_has_next(self): generator = CombinationOfListsGenerator([[1, 2], [3, 4]], 1) self.assertEquals(4, generator.total()) self.assertTrue(generator.has_next()) self.assertEquals([1, 3], generator.next()) self.assertTrue(generator.has_next()) self.assertEquals([1, 4], generator.next()) self.assertTrue(generator.has_next()) self.assertEquals([2, 3], generator.next()) self.assertTrue(generator.has_next()) self.assertEquals([2, 4], generator.next()) self.assertFalse(generator.has_next()) generator.next() self.assertTrue(generator.has_next()) def test_reset(self): generator = CombinationOfListsGenerator([[1, 2], [3, 4]], 1) self.assertEquals(4, generator.total()) self.assertEquals([1, 3], generator.next()) generator.reset() self.assertEquals([1, 3], generator.next()) def test_current(self): generator = CombinationOfListsGenerator([[1, 2], [3, 4]], 1) self.assertEquals(4, generator.total()) self.assertEquals([], generator.current()) self.assertEquals([1, 3], generator.next()) self.assertEquals([1, 3], generator.current()) self.assertEquals([1, 4], generator.next()) self.assertEquals([1, 4], generator.current())

segunda-feira, 6 de setembro de 2010

Python Decorator para imprimir o desempenho de um método

Decorators do Python são bem legais, né? É uma solução elegante, simples e 100% OO. Olha um decorator que fiz para imprimir o desempenho de um método (sugestões são sempre bemvindas):


Exemplo de uso:
@print_performance def minha_funcao(): pass
Implementação:
try: from functools import wraps except ImportError: # Python 2.3, 2.4 fallback. from django.utils.functional import wraps import time def print_performance(function): def wrapped_function(*args, **kwargs): print('START %s(%s)' % (function.__name__, args,)) start_time = time.clock() exception = None try: result = function(*args, **kwargs) except Exception, e: exception = e end_time = time.clock() if exception: print('END-ERROR %s(%s): %ss' % (function.__name__, args, str(end_time - start_time))) raise exception print('END %s(%s): %ss' % (function.__name__, args, str(end_time - start_time))) return result return wraps(function)(wrapped_function)

Combinação(n, r) em Python

Gerar todas combinações possíveis de uma coleção de ítens é muito útil para jogos, por exemplo, para criar uma inteligência artificial para o computador. Python oferece funções prontas pra isso, como pode ser visto em http://docs.python.org/library/itertools.html. De qualquer modo, em alguma situação pode ser útil que as combinações sejam geradas iterativamente, uma de cada vez, como é sugerido pelo algoritmo de Rosen. Segue uma implementação que fiz desse algoritmo:


import math class CombinationGenerator(object): def __init__(self, n, r): if n < 1: raise Exception('Invalid argument: n (%d) must be greater than 1.' % (n,)) elif r < 1: raise Exception('Invalid argument: r (%d) must be greater than 1.' % (r,)) elif r > n: raise Exception('Invalid argument: r (%d) must be lower or equal than n (%d)' % (r, n)) self.n = n self.r = r # total = n! / r! * (n - r)! self._total = math.factorial(n) / (math.factorial(r) * math.factorial(n-r)) self._current = [] def first_combination(self): return list(range(self.r)) def last_combination(self): return list(range(self.n - self.r, self.n)) def __get_index_of_last_valid_element_to_increment(self): for index in reversed(range(self.r)): possibleValue = self._current[index] + 1 possibleValueIsValid = (possibleValue <= (self.n - (self.r - index))) if possibleValueIsValid: return index return None def total(self): return self._total def reset(self): self._current = [] def current(self): return self._current def has_next(self): return self._current != self.last_combination() # Algorithm from Rosen def next(self): ''' Return a list of indexes ''' if len(self._current) == 0: self._current = self.first_combination() else: theIndex = self.__get_index_of_last_valid_element_to_increment() if theIndex == None: self._current = self.first_combination() else: self._current[theIndex] += 1 ref = self._current[theIndex] for index in range(theIndex + 1, self.r): ref += 1 self._current[index] = ref return self._current

Testes de Unidade:
import unittest class CombinationGeneratorTests(unittest.TestCase): def test_1Choose1(self): generator = utilities.CombinationGenerator(1, 1) self.assertEquals(1, generator.total()) self.assertEquals([0], generator.next()) self.assertEquals([0], generator.first_combination()) self.assertEquals([0], generator.last_combination()) def test_NChooseN(self): generator = utilities.CombinationGenerator(3, 3) self.assertEquals(1, generator.total()) self.assertEquals([0, 1, 2], generator.next()) self.assertEquals([0, 1, 2], generator.first_combination()) self.assertEquals([0, 1, 2], generator.last_combination()) def test_2Choose1(self): generator = utilities.CombinationGenerator(2, 1) self.assertEquals(2, generator.total()) self.assertEquals([0], generator.next()) self.assertEquals([1], generator.next()) self.assertEquals([0], generator.first_combination()) self.assertEquals([1], generator.last_combination()) def test_3Choose1(self): generator = utilities.CombinationGenerator(3, 1) self.assertEquals(3, generator.total()) self.assertEquals([0], generator.next()) self.assertEquals([1], generator.next()) self.assertEquals([2], generator.next()) def test_3Choose2(self): generator = utilities.CombinationGenerator(3, 2) self.assertEquals(3, generator.total()) self.assertEquals([0, 1], generator.next()) self.assertEquals([0, 2], generator.next()) self.assertEquals([1, 2], generator.next()) def test_4Choose3(self): generator = utilities.CombinationGenerator(4, 3) self.assertEquals(4, generator.total()) self.assertEquals([0, 1, 2], generator.next()) self.assertEquals([0, 1, 3], generator.next()) self.assertEquals([0, 2, 3], generator.next()) self.assertEquals([1, 2, 3], generator.next()) def test_4Choose2(self): generator = utilities.CombinationGenerator(4, 2) self.assertEquals(6, generator.total()) self.assertEquals([0, 1], generator.next()) self.assertEquals([0, 2], generator.next()) self.assertEquals([0, 3], generator.next()) self.assertEquals([1, 2], generator.next()) self.assertEquals([1, 3], generator.next()) self.assertEquals([2, 3], generator.next()) def test_5Choose3(self): generator = utilities.CombinationGenerator(5, 3) self.assertEquals(10, generator.total()) self.assertEquals([0, 1, 2], generator.next()) self.assertEquals([0, 1, 3], generator.next()) self.assertEquals([0, 1, 4], generator.next()) self.assertEquals([0, 2, 3], generator.next()) self.assertEquals([0, 2, 4], generator.next()) self.assertEquals([0, 3, 4], generator.next()) self.assertEquals([1, 2, 3], generator.next()) self.assertEquals([1, 2, 4], generator.next()) self.assertEquals([1, 3, 4], generator.next()) self.assertEquals([2, 3, 4], generator.next()) self.assertEquals([0, 1, 2], generator.first_combination()) self.assertEquals([2, 3, 4], generator.last_combination()) def test_Sanity15Choose4(self): generator = utilities.CombinationGenerator(15, 4) for x in range(30): combination = generator.next() self.assertEquals(4, len(combination)) for y in combination: self.assertTrue(y >= 0 and y < 15) def test_Sanity50Choose7(self): generator = utilities.CombinationGenerator(50, 7) for x in range(100): combination = generator.next() self.assertEquals(7, len(combination)) for y in combination: self.assertTrue(y >= 0 and y < 50) def test_invalid_input(self): self.assertRaises(Exception, utilities.CombinationGenerator, 0, 0) self.assertRaises(Exception, utilities.CombinationGenerator, -2, -1) self.assertRaises(Exception, utilities.CombinationGenerator, 1, 2) self.assertRaises(Exception, utilities.CombinationGenerator, 2, 0) def test_performance(self): n = 16 # Java Rosen algorithm 22 in 1 second for r in range(1, n+1): generator = utilities.CombinationGenerator(n, r) while generator.has_next(): indexes = generator.next() def test_AfterLastCombinationRestart(self): generator = utilities.CombinationGenerator(3, 2) self.assertEquals([0, 1], generator.next()) self.assertEquals([0, 2], generator.next()) self.assertEquals([1, 2], generator.next()) self.assertEquals([0, 1], generator.next()) def test_has_next(self): generator = utilities.CombinationGenerator(3, 2) self.assertTrue(generator.has_next()) generator.next() self.assertTrue(generator.has_next()) generator.next() self.assertTrue(generator.has_next()) generator.next() self.assertFalse(generator.has_next()) generator.next() self.assertTrue(generator.has_next()) def test_reset(self): generator = utilities.CombinationGenerator(3, 2) self.assertEquals([0, 1], generator.next()) generator.reset() self.assertEquals([0, 1], generator.next()) def test_current(self): generator = utilities.CombinationGenerator(3, 2) self.assertEquals([0, 1], generator.next()) self.assertEquals([0, 1], generator.current()) self.assertEquals([0, 2], generator.next()) self.assertEquals([0, 2], generator.current())

domingo, 29 de agosto de 2010

Django Pluggables

Alguns Django pluggables para funcionalidades rotineiras em Websites.

DjangoOpenID (http://code.google.com/p/djangoopenid): Para facilitar a integração do OpenID para autenticação em aplicações com Django. É uma solução bem simples, não indicado se você precisa de uma solução mais elaborada.

Django-WorldDB (http://code.google.com/p/django-worlddb): Para facilitar a criação de caixas de seleção “país/região/cidade”.

Django-ip2geo (http://code.google.com/p/django-ip2geo): Para descobrir o local de um usuário através de seu IP. Usa a base de dados livre do Maxmind.

Fontes:

http://www.maxmind.com/app/geolitecity
http://www.maxmind.com/app/python
http://www.maxmind.com/app/api
http://www.maxmind.com/app/installation?city=1
http://code.google.com/p/pygeoip/
http://www.ip2phrase.com/


Sua versão Django na Locaweb

Os servidores Linux da Locaweb, assim como os do Google App Engine, fornecem uma versão padrão do Django (0.96, 1.0 ..). Assim, basta configurar sua aplicação Django e pronto (http://wiki.locaweb.com.br/pt-br/Como_instalar_uma_aplicação_Django%3F). O detalhe é que o desenvolvimento do Django é muito ativo, vire e mexe tem versão nova e com MUITAS funcionalidades bacanas. Portanto, compensa sempre utilizar as versões mais novas do framework.

O problema é que nenhuma empresa (tem alguma?) consegue manter uma equipe para gerenciar milhares de versões de milhares de aplicativos de um servidor. É o que ocorre com a Locaweb e também com o Google (no caso do Google App Engine).

A melhor maneira para resolver isso é a solução proposta pelo Google App Engine, que também é aplicável nos servidores da Locaweb. Basta copiar o diretório da versão do Django que desejar na raiz do seu projeto, como se fosse um pluggable (Django = simplicidade + flexibilidade), e configurar o classpath apropriadamente. Resumindo a parte da configuração, basta utilizar o index.wsgi a seguir: 

Importante: Faça backup do index.wsgi original antes de qualquer mudança. import os, sys, site # Using Django of your project directory: # Remove the standard version of Django. for k in [k for k in sys.modules if k.startswith('django')]: del sys.modules[k] # Force sys.path to have our own directory first, in case we want to import from it. sys.path.insert(0, '/home/SEUUSUARIO/wsgi_apps/SEUPROJETO') sys.path.append('/home/ginlab/wsgi_apps/SEUPROJETO') os.environ['DJANGO_SETTINGS_MODULE']='settings' # se usa outras bibliotecas site.addsitedir('/home/SEUUSUARIO/.python/lib') import django.core.handlers.wsgi application = django.core.handlers.wsgi.WSGIHandler() Fontes: http://code.google.com/appengine/articles/django.html

Sistema de autenticação/autorização das redes sociais

Para muitos sistemas, compensa mais utilizar um sistema de autenticação/autorização dos WebServices de redes sociais famosas (semelhante ao sistema de OpenID) do que implementar um módulo do zero. O primeiro passo é a criação de "contas de aplicação" (análogo a contas de usuário) nas redes sociais. Segue as URLs para cadastrar uma aplicação nas redes sociais mais famosas:

Facebook: http://www.facebook.com/developers/createapp.php
Twitter: http://twitter.com/apps
Orkut/Gmail/Google: https://www.google.com/accounts/ManageDomains
LinkedIn: http://developer.linkedin.com/docs/DOC-1008

Ainda, existem plugins que facilitam a integração dessas funcionalidades ao seu framework MVC-Web. Por exemplo, para Python-Django:

Social-Auth:
http://uswaretech.com/blog/2009/08/django-socialauth-login-via-twitter-facebook-openid-yahoo-google/
http://github.com/uswaretech/Django-Socialauth

PyFacebook:
http://code.google.com/p/pyfacebook/

sábado, 28 de agosto de 2010

Comparações entre linguagens

Quando vamos discutir sobre linguagens de programação, alguns comportamentos são típicos: Uns tentam puxar a sardinha pro que lhe convém, outros fogem da raia e dizem que não existe uma linguagem perfeita e fim de papo.
Hoje estamos mais evoluidos e novos paradigmas estão surgindo. A interminável guerra entre tipagem estática ou dinâmica está a ponto de receber um pouco mais de emoção com o surgimento do JRuby e Jython que misturam os dois paradigmas. Também está ficando mais interessante as discussões sobre conceitos de programação, como orientação a objetos, programação funcional, orientação a aspectos e reflexão que podem ser utilizados juntos e de forma amigável, principalmente com linguagens com bom suporte como Smalltalk, Python, Ruby e Scala, entre outras.

Minha opinião é que pode existir sim uma linguagem perfeita, que atenda a todas a necessidades, mas ainda deve demorar para chegarmos as conclusões pertinentes para isso. Para pensarmos sobre isso, temos que pensar o que esperamos de uma linguagem, destaco algumas características interessantes:

- Performance
- Produtividade
- Sintaxe bonita
- Boas IDEs
- API completa, extensível e fácil de usar
- Frameworks produtivos e completos
- Suporte a orientação a objetos
- Suporte a orientação a aspectos
- Suporte a programação funcional
- Suporte a reflexão
- Tipagem estática ou dinâmica ou as duas
- Bom suporte a programação concorrente ou paralela
- Interpretada ou compilada
- Multi-plataforma
- Código aberto
- ente infinitos outros

Se formos pensar só em performance, escolheriamos C++ ou Lua, pensando em sintaxe bonita escolheriamos Python, considerando frameworks produtivos escolheriamos Ruby (por causa do Rails), bom suporte a programação concorrente ou paralesla optaríamos por Scala, boa IDE escolheríamos Java (com Eclipse + JUnitMax) e por ai vai.

Destas características, algumas são incostestáveis, por exemplo, todos querem melhor performance. Em relação às APIs e frameworks também. Quem não prefere fazer print("alguma coisa") ao invés de System.out.println("alguma coisa")?? Lembro que quando comecei a aprender Java eu achava lindo fazer "new BufferedReader(new FileReader(new InputStreamReader(new InternalAlgumaCoisaReader(new BytecodeReader(..." porque sabia que a orientação a objetos por trás desta API estava muito bonita, mas depois de um tempo isto me torrou a paciência.
Já em relação aos conceitos de programação, todos são compatíveis e podem trabalhar juntos em perfeita harmonia, vide exemplos como Java + AspectJ, Hibernate + Reflection, Ruby OO + Ruby funcional, etc..
O que acho que é o mais crítico e difícil de chegar a uma conclusão é em relação a tipagem. Escrever código com tipagem dinâmica é muito mais gostoso, por outro lado trabalhar com o auxílio do compilador também é, não é uma delícia dar control+espaço e a IDE completa o resto pra gente? E agora? Alguns usam como argumentos a facilidade de refatoração, por exemplo Java possui um suporte a refatoração esplêndido, mas SmallTalk e Python também! Então fica complicado chegar a alguma conclusão a partir deste argumento.

Bom, enfim, o objetivo deste post não era concluir nada, é só para aguçar a discussão e talvez estimular posts como "Scala vs Java", "Scala vs Python", "Scala vs Ruby", "Scala vs Haskell", "Scala vs Groovy", que acho uma excelente maneira da gente entender melhor os pontos fortes e fracos das linguagens.

Plugins pro Eclipse

Python:

Java:
ajdt (AspectJ): http://download.eclipse.org/tools/ajdt/34/update
maven: http://m2eclipse.sonatype.org/update

Scala:
scala: http://www.scala-lang.org/scala-eclipse-plugin-nigthly

Groovy:
groovy/grails: http://dist.codehaus.org/groovy/distributions/update/

Métricas:
eclemma: http://update.eclemma.org
coverclipse: http://coverlipse.sf.net/update
metrics: http://www.stateofflow.com/UpdateSite

Testes:
testng: http://beust.com/eclipse
jsunit: download http://sourceforge.net/projects/jsunit/files/

Controle de Versão:
subclipse: http://subclipse.tigris.org/update_1.0.x

Bibliotecas Python

Básicas:
sudo apt-get install python-setuptools

Testes:
sudo easy_install mock

Redes sociais, autenticação:
sudo easy_install python-openid
sudo easy_install python-yadis
sudo easy_install oauth

Biblioteca de manipulação de imagens:
Antes de instalar PIL, instalar dependências, senão dá problema:
sudo apt-get install libfreetype6-dev libjpeg-dev libfreetype6 zlib1g-dev liblcms1-dev
sudo easy_install PIL
sudo apt-get install python-imaging

Fontes:
Bug PIL: http://www.answermysearches.com/fixing-pil-ioerror-decoder-jpeg-not-available/320/