Image Image Image Image Image
Scroll to Top

Topo

Python

SWX Labs 06 – Python

Em 17, fev 2014 | Sem Comentários | Em Blog, Código, Destaques, Open Source, Python, SWX Labs | Por Mike Lopes

capa01

No SWX Labs 06 Mike, Vinicius e Tássio conversam com Rodrigo Amaral sobre Python.

Como surgiu a linguagem, qual sua evolução, diferenciais, onde ela é aplicada e como funciona a comunidade que divulga e dá suporte a uma das linguagens de programação que mais cresce e se credencia a ser o Java do futuro.

Ouça o podcast clicando no play, se preferir faça o download

Temas abordados

  • História do Python
  • Particularidades da Sintaxe
  • Ferramentas e aplicações
  • Mercado de trabalho
  • Comunidade e eventos sobre Python

Links

2º GDG Aracaju: Aplicações web com Python e Google App Engine

gdg-aju-python

Assine o feed do nosso podcast e não perca nenhum episódio: http://feeds.feedburner.com/SWXLabs

Se preferir, também estamos no Itunes Store

Gostou? Não gostou? Erramos algo? Sua opinião é muito importante para nós, por isso deixe seu comentário ou envie email para mike@swx.com.br

Tags | , , ,

27

abr
2012

Sem Comentários

Em Blog
Python

Por Allison

Introdução aos “decorators” do Python

Em 27, abr 2012 | Sem Comentários | Em Blog, Python | Por Allison

Fonte: IMasters

Texto original disponível em: http://www.artima.com/weblogs/viewpost.jsp?thread=240808

Este recurso incrível apareceu na linguagem quase se desculpando, e com preocupações de que poderia não ser tão útil. Eu, por minha vez, acredito que em tempo ele será visto como um dos recursos mais poderosos na linguagem. O problema é que todas as introduções à decorators que eu já vi foram bastante confusas, então vou tentar consertar isso neste artigo.

Decorators vs. decorators pattern

Primeiro, você precisa entender que a palavra “decorator” foi usada com certo receio, porque existia uma preocupação em que confundissem o decorator pattern do livro Design Patterns. Outros termos foram considerados para substituí-lo, mas “decorator” parece ser o escolhido.

Na verdade, você pode utilizar os decorators do Python para implementar um decorator pattern, mas esse é um uso muito limitado dele. Os decorators do Python, acredito, se equivalem mais aos macros.

História dos macros

O macro tem uma longa história, mas a maioria das pessoas provavelmente tiveram experiências com os macro processadores C. Os problemas com os macros C eram os seguintes: eles estavam em uma linguagem diferente da C, e o comportamento deles, às vezes, era bizarro e inconsistente com o comportamento do resto do C.

Tanto o Java, quanto o C# adicionaram anotações, que permitem que você faça algumas coisas para os elementos da linguagem. Ambos têm problemas para fazer o que você quer. Às vezes você tem que pular através de um círculo grande e complexo. Há também o fato de que esse recurso de anotações tem suas mãos atadas pela escravidão e disciplina (ou como Martin Fowler gentilmente coloca: “Direcionamento”) natural dessas linguagens.

Muitos programadores C++ (inclusive eu) notaram as habilidades geradoras de templates C++ e utilizaram esse recurso de uma maneira macro.

Muitas outras linguagens incorporaram os macros. Eu, ainda sem saber muito sobre eles, ouso a dizer que os decoradores do Python são parecidos com os macros do Lips em poder e em possibilidades.

O objetivos dos macros

Acredito que seja certo dizer que o objetivo dos marcos em uma linguagem é fornecer uma maneira de modificar os elementos da mesma. É isso que os decorators fazem no Python: eles modificam funções. É por isso que eles normalmente fornecem uma alternativa mais simples para metaclasses.

As maiores falhas da maioria das abordagens de modificações próprias das linguagens são que elas são muito restritivas e que precisam de uma linguagem diferente (posso dizer que as anotações Java compreendem uma “linguagem diferente”, ainda que com todos os problemas pelos quais você deve passar para produzir uma anotação interessante).

O Python cai na categoria de Fowler de “habilitar” linguagens, então se você quiser fazer modificações, por que criar uma linguagem diferente, ou restrita? Por que simplesmente não usar o Python? E é isso que os decorators de Python fazem.

O que você pode fazer com decorators?

Decorators permitem que você insira, ou modifique o código em funções, ou classes. Parece um pouco como Aspect-Oriented Programming (AOP) em Java, não? Exceto que é muito mais simples e poderoso. Por exemplo, suponha que você gostaria de fazer algo nos pontos de entrada e saída de uma função (como executar algum tipo de segurança, rota, bloqueio, etc – todos os argumentos padrões para AOP). Com decorators, fica assim:

@entryExit
def func1():
print "inside func1()"
 
@entryExit
def func2():
print "inside func2()"
O @ indica a aplicação do decorator.

Decorators de funções

O decorator de uma função é aplicado a uma definição de função ao ser colocado na linha antes da que começa a definição da função. Por exemplo:

@myDecorator
def aFunction():
print "inside aFunction"

Quando o compilador passa por este código, a aFunction() é compilada e o objeto da função resultante é passado para o código myDecorator, que faz algo para produzir um objeto do tipo função, que é, então, substituído pelo original aFunction().

Com o que o código myDecorator se parece? Bom, a maior parte dos exemplos introdutórios o mostra como uma função, mas eu achei mais fácil começar a compreender os decorators usando classes como mecanismos de decoração, ao invés de funções. Além disso, é mais poderoso.

O único lado ruim de um objeto retornado por um decorator é que ele pode ser usado como uma função – o que basicamente significa que ele deve ser chamável. Assim, quaisquer classes que usarmos como descoradores devem implementar __call__.

O que um decorator faz? Bem, ele pode fazer qualquer coisa, mas normalmente você espera que o código da função original seja usado em algum ponto. Isso não é necessário, no entanto:

class myDecorator(object):
 
def __init__(self, f):
print "inside myDecorator.__init__()"
f() # Prove that function definition has completed
 
def __call__(self):
print "inside myDecorator.__call__()"
 
@myDecorator
def aFunction():
print "inside aFunction()"
 
print "Finished decorating aFunction()"
 
aFunction()

Quando você rodar esse código, o verá assim:

inside myDecorator.__init__()
inside aFunction()
Finished decorating aFunction()
inside myDecorator.__call__()

Note que o construtor para myDecorator é executado no ponto da decoração da função. Como podemos chamar f() dento de __init__(), isso mostra que a criação de f() está completa antes do decorator ser chamado. Note, também, que o construtor do decorator recebe o objeto da função sendo decorado. Normalmente, você irá capturar o objeto da função no construtor e depois utilizá-lo no método __call__() (o fato de que decoração e chamado são duas fases claras quando se utiliza classes, é o motivo pelo qual eu argumento que esse é o modo mais fácil e mais poderoso).

Quando aFunction() é chamada depois de ter sido decorada, temos um comportamento completamente diferente; o método myDecorator.__call__() é chamado, ao invés do código original. Isso acontece porque o ato da decoração substitui o objeto da função original com o resutlado da decoração – no nosso caso, o objeto myDecorator substitui aFunction. De fato, antes dos decorators serem adicionados, você tinha que fazer algo muito menos elegante para alcançar a mesma coisa:

def foo(): pass
foo = staticmethod(foo)

Com a adição do operador de decoração @, agora você tem o mesmo resultado dizendo:

@staticmethod
def foo(): pass

Esse é o motivo pelo qual as pessoas argumentam contra os decorators, porque o @ é apenas uma sintaxe simplificada, que significa “passar um objeto de função, através de outra função, e atribuir o resultado à função original”.

Acredito que a razão pela qual os decorators sentem tanto o impacto é porque essa maneira mais fácil, também conhecida como “syntactic sugar“, muda o modo como você pensa programação. Certamente, ele traz a ideia de “aplicar código a outro código”(por exemplo, macros) no pensamento dominante ao formalizá-lo como um construtor de linguagem.

Levemente mais útil

Agora, vamos voltar e implementar o primeiro exemplo. Aqui, vamos fazer a coisa mais comum e de fato utilizar o código nas funções decoradoras:

class entryExit(object):
 
def __init__(self, f):
self.f = f
 
def __call__(self):
print "Entering", self.f.__name__
self.f()
print "Exited", self.f.__name__
 
@entryExit
def func1():
print "inside func1()"
 
@entryExit
def func2():
print "inside func2()"
 
func1()
func2()
A saída é:
 
Entering func1
inside func1()
Exited func1
Entering func2
inside func2()
Exited func2

Você pode ver que as funções decoradoras agora tem as declarações das linha “Entering” e “Exited” em volta do chamado.

O construtor armazena o argumento, que é o objeto da função. No chamado, usamos o atributo da função __name__ para exibir o nome da função, e então chamá-la.

Usando funções como decorators

A única restrição no resultado de um decorator é que ele é chamável, então ele pode substituir propriamente a função decoradora. Nos exemplos acima, eu substitui a função original com um objeto de classe que tem um método __call__(). Mas um objeto de função também é chamável, então podemos reescrever o exemplo anterior usando uma função, ao invés de uma classe, assim:

def entryExit(f):
def new_f():
print "Entering", f.__name__
f()
print "Exited", f.__name__
return new_f
 
@entryExit
def func1():
print "inside func1()"
 
@entryExit
def func2():
print "inside func2()"
 
func1()
func2()
print func1.__name__

A new_f() é definida dentro do corpo de entryExit(), então ela é criada e retornada quando a entryExit() é chamada. Note que new_f() é um encerramento, porque ela captura o valor real de f.

Uma vez que a new_f() tenha sido definida, ela é retornada a partir de entryExit(), de modo que o mecanismo do decorator possa determinar o resultado como uma função decoradora.

O output da linha print func1.__name__ é new_f, porque a função new_f foi substituída pela função original durante a decoração. Se isso for um problema, você pode mudar o nome da função antes de retorná-la:

def entryExit(f):
def new_f():
print "Entering", f.__name__
f()
print "Exited", f.__name__
new_f.__name__ = f.__name__
return new_f

A informação que você consegue obter dinamicamente sobre as funções, e as modificações que você consegue fazer nelas são bastante poderosas no Python.

Mais exemplos

Agora que você sabe o básico, você pode dar uma olhada em mais exemplos de decorators aqui. Observe o número de exemplos que usam classes, ao invés de funções como decorators.

Neste artigo eu intencionalmente evitei lidar com os argumentos da função decoradora, mas farei isso em um próximo. Arguardem!

Tags | , ,

27

abr
2012

Sem Comentários

Em Blog
Python

Por Allison

Threads em Python

Em 27, abr 2012 | Sem Comentários | Em Blog, Python | Por Allison

Fonte: IMasters

artigo publicado originalmente no developerWorks Brasil, por Breno Leitão

Breno Leitão é engenheiro de software pela IBM onde atua como kernel hacker.


Python é uma linguagem bem genérica e fácil de ser utilizada. Qualquer usuário pode aprender a programar em Python de uma maneira bem fácil, principalmente porque a linguagem encapsula conceitos difíceis em implementações fáceis.

Neste artigo vamos tratar da utilização de threads em Python. Threads são fluxos de programas que executam em paralelo dentro de uma aplicação, isto é, uma ramificação de uma parte da aplicação que é executada de forma independente e escalonada independentemente do fluxo inicial da aplicação.

Imaginemos, por exemplo, uma aplicação que mede, de tempos em tempos, a condição de determinados sensores. Supondo que cada sensor precisa ser medido com uma frequência diferente, isto é, um a cada 30 segundos, outro a cada 45 segundos e, por fim, um terceiro a cada 75 segundos.

Implementar isto de maneira sequencial é trabalhoso. Um jeito fácil, porém, é a implementação de uma thread independente para a leitura em cada um dos sensores. Desta forma a thread espera o tempo determinado para a leitura do sensor a que ela está ligada, sem se preocupar, ou mesmo saber, sobre os outros sensores.

Assim, neste caso, bastaria fazer uma classe por tipo de sensor, sendo que cada classe seria uma thread. Para transformar uma classe em thread, são necessárias duas modificações na classe:

  • A classe em questão estender à classe Thread do pacote threading
  • Implementar o método run(), que será chamado quando a thread iniciar

Em Python, o pacote que providencia as funcionalidades de thread é chamado threading, e deve ser importado no começo do seu programa: from threading import Thread.

Segue um exemplo básico, de uma classe chamada Th que implementa Thread e o método run(). O conteúdo do método run será executado em uma thread separada sempre que o método start, definido na classe Thread e herdado pela classe Th no nosso exemplo, for chamado:

from threading import Thread

class Th(Thread):

def __init__ (self, num):
Thread.__init__(self)
self.num = num

def run(self):

print "Hello "
print self.num

a = Th(1)
a.start()

Apesar de, no exemplo acima, o conteúdo do método run ser executado em uma thread separada, não é possível comprovar isto apenas pela saída do programa.

Afim de comprovarmos que cada thread é executada de forma independente e escalonada independentemente do fluxo inicial da aplicação, vamos analisar o próximo exemplo. Nele criamos várias threads simples Th, como as do exemplo acima, porém ao invés de simplesmente imprimirmos uma mensagem na thread ela vai executar um número definido de vezes COUNTDOWN antes de finalizar sua execução:

from threading import Thread
import sys

COUNTDOWN = 5

class Th(Thread):

def __init__ (self, num):
sys.stdout.write("Making thread number " + str(num) + "n")
sys.stdout.flush()
Thread.__init__(self)
self.num = num
self.countdown = COUNTDOWN

def run(self):

while (self.countdown):
sys.stdout.write("Thread " + str(self.num) +
" (" + str(self.countdown) + ")n")
sys.stdout.flush()
self.countdown -= 1

for thread_number in range (5):

                    thread = Th(thread_number)
                    thread.start()


Uma das possíveis saídas para o programa acima é a seguinte:

Making thread number 0
Thread 0 (5)
Thread 0 (4)
Thread 0 (3)
Thread 0 (2)
Thread 0 (1)
Making thread number 1
Thread 1 (5)
Making thread number 2
Making thread number 3
Thread 2 (5)
Thread 1 (4)
Thread 1 (3)
Thread 2 (4)
Thread 1 (2)
Thread 2 (3)
Thread 1 (1)
Thread 2 (2)
Making thread number 4
Thread 2 (1)
Thread 3 (5)
Thread 4 (5)
Thread 4 (4)
Thread 3 (4)
Thread 4 (3)
Thread 3 (3)
Thread 4 (2)
Thread 3 (2)
Thread 4 (1)
Thread 3 (1)

Caso você rode o programa acima, a saída não necessariamente será igual a esta, já que a alocação das threads para execução no processador não é um processo determinado. Mesmo rodando múltiplas vezes, o mesmo programa em um mesmo computador as saídas irão variar de uma execução para outra.

Um ponto interessante de se notar no exemplo acima é que, em vez de usarmos print para imprimir na saída padrão, utilizamos sys.stdout.write seguido de uma chamada a sys.stdout.flush.

Isto foi feito para garantir que as mensagems fossem impressas em ordem, já que chamadas a print por diversas threads simultaneamente não garantem a ordem de impressão dos caracteres.

Sincronização de threads

Nos exemplos citados anteriormente, usou-se threads para efetuar processamento paralelos distintos e sem ligação entre si. No entanto, no mundo real muitas vezes as diversas linhas de execução representada pelas threads de um programa precisam, eventualmente, comunicar-se entre si.

Uma forma simples de comunicação é aquela que precisa ocorrer no final do processamento das threads. Exemplos típicos deste tipo de necessidade são programas que processam dados em vetores ou matrizes.

Se estes vetores ou matrizes forem muito grandes e os cálculos efetuados em cada elemento relativamente demorados e independentes até certo ponto, a utilização de threads pode acelerar bastante este tipo de cálculo, já que o trabalho é de alguma forma dividido entre as diversas threads.

Ao final do processo, basta realizarmos um cálculo mais simples que agrega os sub-totais calculados pelas threads. Este tipo de sincronização no final da execução de uma thread pode ser feito através do método join da classe Thread.

Por exemplo, imaginemos um programa que soma os valores de um vetor de inteiros com 1000 elementos cujos valores variam de 0 a 100. Para fins deste exemplo, este vetor será criado com valores randômicos.

Serão criadas quatro threads que calcularão a soma de 250 elementos cada uma. Ao fim do processamento, os sub-totais gerados pelas quatro threads serão somados para gerar um único valor total referente à soma de todos os elementos do vetor.

from threading import Thread
import random
import sys

NUM_VALUES = 1000
values = []
sequential_total = 0
threaded_total = 0
threads = []
NUM_THREADS = 4

class Th(Thread):
subtotal = 0

def __init__ (self, num):
sys.stdout.write("Making thread number " + str(num) + "n")
sys.stdout.flush()
Thread.__init__(self)
self.num = num

def run(self):

range_start = self.num * NUM_VALUES / NUM_THREADS
range_end = ((self.num + 1) * NUM_VALUES / NUM_THREADS) - 1

for i in range(range_start, range_end):
self.subtotal += values[i]
sys.stdout.write("Subtotal for thread " + str(self.num) +
": " + str(self.subtotal)
+ " (from " + str(range_start)

+ " to " + str(range_end) + ")n");

sys.stdout.flush()

def get_subtotal(self):
return self.subtotal

#### O programa comeca aqui #####

for i in range(NUM_VALUES):
values.append(random.randint(0,100))

for i in range(NUM_VALUES):
sequential_total += values[i]

print("Sequential total: " + str(sequential_total))

for thread_number in range(NUM_THREADS):
threads.insert(thread_number, Th(thread_number))
threads[thread_number].start()

for thread_number in range(NUM_THREADS):
threads[thread_number].join()
threaded_total += threads[thread_number].get_subtotal()

print("Threaded total: " + str(threaded_total))

Um exemplo de saída para o programa acima seria o seguinte:

Sequential total: 49313
Making thread number 0
Making thread number 1
Subtotal for thread 0: 12365 (from 0 to 249)
Making thread number 2
Subtotal for thread 1: 12568 (from 250 to 499)
Making thread number 3
Subtotal for thread 2: 11742 (from 500 to 749)
Subtotal for thread 3: 12638 (from 750 to 999)
Threaded total: 4931

Note que o valor total calculado pela versão sequencial da soma dos elementos do vetor é igual à soma calculada através das quatro threads criadas no programa para fazer o mesmo trabalho de forma dividida.

Uma outra forma de sincronização pode ser necessária quando há necessidade de se acessar variáveis cujos valores são compartilhados por várias threads rodando simultaneamente. Para isso, é necessário lançar mão da utilização de locks (threading.Lock) ou semáforos (threading.Semaphore). Um artigo a parte descreverá os locks e semáforos, é só aguardar pelo próximo post.

Conclusão

Este artigo fez uma introdução a programação com Thread em python, mostrando o que é uma thread, com deve ser construida classes que tornam-se thread e como sincronizar o fim das threads.

Recursos

Tags | , ,

15

mar
2012

Sem Comentários

Em Blog
Python

Por Allison

Django divulga planos para migrar para Python 3

Em 15, mar 2012 | Sem Comentários | Em Blog, Python | Por Allison

Fonte: IMasters

Com informações de The H

Os desenvolvedores do Django publicaram planos sobre como eles migrarão seu framework baseado em Python para o Python 3. Vários usuários apontaram a falta de suporte para o Python 3 no Django como a razão para não migrarem para a nova versão da linguagem. Trabalhando com a equipe do Python, eles optaram por uma migração organizada, usando o Python 2.6 como ponto de partida.

Primeiramente, os desenvolvedores pretendem diminuir o uso de versões antigas 2.x do Python, até que a versão mínima do Python pelo Django seja a 2.6. Essa versão é a base da transição, já que foi desenvolvida para ser uma ponte para o Python 3, uma vez que muitas funcionalidades do mais novo Python foram portadas novamente para ela, que inclui ferramentas para dar suporte para a migração de código para o Python 3.

O ainda não lançado Django 1.4 não terá mais suporte para Python 2.4, e o Django 1.5, por sua vez, deixará de suportar o Python 2.5 mais para frente. Além disso, o Django 1.5 vai adicionar suporte experimental para o Python 3.x, principalmente a versão 3.3. Logo, o Django 1.5 será o início do processo de portar o código base do Django para o Python 3.

Detalhes sobre essa transição ainda não estão disponíveis, mas, segundo os desenvolvedores do Django, o Python 3 é o futuro da linguagem Python e, em apoio a isso, o Django continuará comprometido em alcançar a compatibilidade com o Python.

Tags | , , ,

05

mar
2012

Sem Comentários

Em Blog
Python

Por Allison

Alfa do Python 3.3.0 traz primeiras mudanças sintáticas em dois anos

Em 05, mar 2012 | Sem Comentários | Em Blog, Python | Por Allison

Fonte: IMasters

Com informações de The H

A versão 3.3.0 da linguagem de programação Python entrou na fase de testes com a liberação do seu primeiro alfa. Ela marca o fim de dois anos sem mudanças na sintática na linguagem. Propostas por Guido van Rossum como Python Enhancement Proposal (PEP) 3003, as mudanças foram feitas para habilitar implementações não-CPython da linguagem para atualizar a implementação do core depois do lançamento do Pyhton 3.0.

Com as mudanças na sintaxe permitidas novamente, o alfa inclui uma nova maneira de habilitar generators a delegar trabalho para sub-generators, o que abre novas possibilidades para otimizar iterações no código do Python. Outra mudança na sintaxe adiciona o atributo __qualname__ a funções e classes. Isso possibilita dizer se o objeto está no topo da hierarquia ou se ele é aninhado dentro de outro objeto.

O PEP 393 faz uma representação de string mais flexível ao mudar o tipo de string Unicode para permitir várias representações internas ao mesmo tempo. Com isso, os programadores podem otimizar espaço ou maximizar a disponibilidade de caracteres e abandonar a distinção entre builds estreitos e amplos do Unicode.

Além disso, a sintaxe lateral do Unicode a partir do Python 2 foi reintegrada ao Python 3.3.0 para tornar a migração dos aplicativos do Pyhton 2 mais fácil. Outras mudanças na nova versão incluem melhorias em lidar com exceção, um novo módulo de “empacotamento” e suporte para compressão LZMA/XZ.

Mais detalhes sobre as modificações podem ser encontrados nas notas de lançamento e neste link. O Python 3.3.0 alfa 1 está disponível para download para Windows e Mac OS X.

Tags | , , , , ,

28

fev
2012

Sem Comentários

Em Blog
Python

Por Allison

Python 2.7 está disponível no App Engine do Google

Em 28, fev 2012 | Sem Comentários | Em Blog, Python | Por Allison

Com informações de The H

Fonte: IMasters

O Google anunciou que o Python 2.7 está disponível em sua plataforma App Engine como uma funcionalidade com suporte completo. A versão 2.7 traz várias melhorias ao suporte do App Engine para Python, como suporte para multithreading, para lidar com requisições simultâneas, para fazer o upload do bytecode do Python, para gerar o bytecode e para manipular bibliotecas e JSON nativo. Segundo a empresa, o multithreading e o suporte para requisições simultâneas devem permitir que os desenvolvedores usem completamente a CPU.

Além disso, os desenvolvedores do Google adicionaram a Python Imaging Library (PIL), a biblioteca de computação científica NumPy, e a biblioteca de processamento em XML/HTML lxml ao tempo de execução da App Engine para Python. O novo tempo de execução suporta apenas a versão 1.2 do framework Django – as versões anteriores suportavam Django 0.96 e 1.2 – logo, os desenvolvedores terão que atualizar seus aplicativos baseados em Django. O suporte para Djangoforms foi removido, e o Google sugere o WTForms como alternativa.

Outras modificações estão listadas neste guia. Um novo App Engine SDK para Python está disponível para download. A App Engine é um sistema cobrado comercialmente, mas o Google afirma que aplicativos podem utilizar 1 GB de armazenamento e “CPU e largura de banda suficientes para suportar uma entrega eficiente de cerca de 5 milhões de page views por mês” gratuitamente.

O Python 2.7 é o último branch da série Python 2, que será sucedida pelo Python 3, cujo lançamento ainda não tem previsão.

Tags | , , ,

17

fev
2012

Sem Comentários

Em Blog
Python

Por Allison

Django 1.4 beta 1: uma previsão da nova versão do maior framework Python

Em 17, fev 2012 | Sem Comentários | Em Blog, Python | Por Allison

Fonte: Eder Magalhães/IMasters

A comunidade de desenvolvimento do Django anunciou a versão 1.4 beta 1, uma amostra com as novas funcionalidades e melhorias da próxima versão do Django. O Django é um popular framework open source, para desenvolvimento web utilizando Python. Disponibiliza uma estrutura alto-nível para Mapeamento Objeto/Relacional (ORM, na sigla em inglês), além da definição de templates visuais, criação automática de interfaces para administração de conteúdo, e outras características como foco em produtividade.

A nova versão do Django implementa o conjunto extenso de melhorias, entre elas:

  • Integração com ferramentas de testes. O Django agora pode ser integrado a ferramentas de testes direto do navegador. Com a nova classe LiveServerTestCase programadores poderão escrever casos de testes que operam como um serviço e são acionados no navegador por clientes/scripts de testes, como o Selenium.
  • Novidades em ORM. Através do método bulk_create, o mecanismo de ORM do Django suporta a criação de múltiplos objetos, aumentando muito a performance ao utilizar muitas instâncias de objetos. Já com o novo método prefetch_related é possível obter relacionamentos complexos entre objetos de forma mais eficaz. Outra novidade no ORM do Django é o suporte a locks pessimistas de registros na base de dados, através do método select_for_update.
  • Melhorias em segurança. O Django 1.4 introduz um novo mecanismo para armazenamento de senhas com algoritmo PBKDF2 e codificação SHA256, mais seguro que a alternativa anterior (o Django 1.3 utiliza SHA1). Também foram implementadas APIs de criptografia, permitindo a manipulação de cookies assinados.
  • Internacionalização. Com o novo método i18n_patterns a nova versão do framework utiliza um prefixo na URLs mapeadas da aplicação, identificando o idioma em que o conteúdo deve ser apresentado. Adicionalmente é possível traduzir o conteúdo de uma URL através do método ugettext_lazy.
  • Templates. A versão 1.4 traz a função assignment_tag para simplificar a criação de tags que manipulam variáveis no contexto da aplicação. Outra novidade é que, a partir de agora, exceções lançadas em modo de depuração não são mais encapsuladas em TemplateSyntaxError.

Por fim, é importante notar que a partir da versão 1.4, o Django passa a suportar o Python 2.5 como a versão mínima da linguagem.

Para experimentar o Django 1.4 beta 1, é necessário realizar o download no site do projeto. O objetivo é que, ao testarem a versão beta, os desenvolvedores colaborem, fornecendo o feedback sobre as funcionalidades implementadas além de identificar e corrigir eventuais bugs. O lançamento do Django 1.4 final deve ocorrer até o fim de março deste ano.

Tags | , , , ,

31

jan
2012

Sem Comentários

Em Blog
Python

Por Allison

Como Trabalhar com Threads em Python

Em 31, jan 2012 | Sem Comentários | Em Blog, Python | Por Allison

Fonte: Rafael Cirolini/NerdHead

Precisei criar um programa (futuramente vou colocar ele aqui) que precisava testar 290 links de uma pagina para saber se eles estavam ok. O teste era simples, tentar conectar e esperar um 200 OK da resposta do servidor para verificar se não havia problema.

O problema consistia que o teste deveria ser razoavelmente rápido, e fazendo os testes de forma sequenciais demorava em torno de 16 minutos, o que não era aceitável. Uma solução foi a criação de threads para paralelizar o trabalho. Foi difícil achar alguma documentação que explique de forma simples o que eu precisava fazer, que era disparar o maior numero de processos simultâneos, aguardar eles terminarem e fazer diversas verificações.

Bom, aqui pretendo demonstrar de maneira bem simples como criar uma fila (queue), e criar threads para consumi-la.

#imports necessários
import random
from time import sleep
from Queue import *
from threading import Thread, Lock
 
#função para consumir a fila
def consumir():
    job = queue.get() #pega o primeiro job livre
    sleep(random.randint(1,3)) #espera
    print "Estou consumindo o job: %s"%(job)
    queue.task_done() #finaliza o job
 
#gera a fila e coloca todos os links na fila
queue = Queue()
 
jobs = 0
while jobs < 10:
    print "Colocando o job %s na fila"%(jobs)
    queue.put(jobs)
    jobs = jobs + 1
 
print "A fila tem %s jobs"%(queue.qsize())
 
#enquanto tiver fila cria uma tread
i = 0
while i < queue.qsize():
    th = Thread(target=consumir)
    th.setDaemon(True)
    th.start()
 
#espera a fila ser consumida para saber se esta com algum erro
queue.join()
print "Finalizou"

A saída deve ser algo mais ou menos assim:

Colocando o job 0 na fila

Colocando o job 1 na fila

Colocando o job 2 na fila

Colocando o job 3 na fila

Colocando o job 4 na fila

Colocando o job 5 na fila

Colocando o job 6 na fila

Colocando o job 7 na fila

Colocando o job 8 na fila

Colocando o job 9 na fila

A fila tem 10 jobs

Estou consumindo o job: 9

Estou consumindo o job: 1

Estou consumindo o job: 4

Estou consumindo o job: 6

Estou consumindo o job: 0

Estou consumindo o job: 2

Estou consumindo o job: 3

Estou consumindo o job: 5

Estou consumindo o job: 7

Estou consumindo o job: 8

Finalizou

Usei o random e o sleep somente para mostrar que os jobs são consumidos de maneira assíncrona e que não esperam um pelo outro para serem consumidos.

Tags | , , ,

18

jan
2012

Sem Comentários

Em Blog
Python

Por Allison

Web Service em Python

Em 18, jan 2012 | Sem Comentários | Em Blog, Python | Por Allison

Fonte: Rafael Cirolini/NerdHead

Tive uma necessidade estes dias de criar um webservice em um servidor linux que retornasse os logs de emails do postfix, fazendo um grep (find) pelo usuário, tipo de protocolo e data.

Pesquisei um pouco, e achei uma solução muito boa, fazer o desenvolvimento do webservice em Python, o que é muito simples e de muito rápida execução.

O servidor que eu estava trabalhando usa CentOS e vinha com as bibliotecas padrões do python instalado, caso o seu sistema não venha, um apt-get install python ou um yum install python deve resolver o problema. Mas eu ainda precisava de mais duas bibliotecas que não vinham com a minha distro: fpconst e SOAPpy. Procurei as duas aqui: http://rpm.pbone.net/, achei a que se enquadrava para o meu sistema operacional i586.

Segue as versões de como eu usei:

mkdir /home/pacotes
cd /home/pacotes/
wget http://pypi.python.org/packages/source/f/fpconst/fpconst-0.7.2.tar.gz#md5=10ba9e04129af23108d24c22c3a698b1
wget http://downloads.sourceforge.net/project/pywebsvcs/SOAP.py/0.12.0_rc1/SOAPpy-0.12.0.tar.gz?use_mirror=ufpr
tar -cvf fpconst-0.7.2.tar.gz
tar -cvf SOAPpy-0.12.0.tar.gz
cd fpconst-0.7.2
python setup.py install
cd ../home/SOAPpy-0.12.0
python setup.py install

Pronto, minhas duas bibliotecas já estavam funcionando.

Agora vamos ao webservice:

#!/usr/bin/python
#import das bibliotecas necessarias
from SOAPpy import SOAPServer
import os
import commands
 
#caminho dos arquivos de logs
caminho = '/root/cirolini/'
 
#identifica se o protocolo for pop ou imap e direciona para a pasta solicitada
def protocolo(protocolo):
        if protocolo == 'popper':
                return 'popper/popper.log'
        if protocolo == 'imap':
                return 'imap/imap.log'
 
#literalmente da um grep nos logs que estao no formato popper.log.2010-03-16T08:00-03:00
def buscaLogs(usuario, proto, data):
        proto = protocolo(proto)
        busca = "zgrep " + usuario + " " + proto + "." + data +"*"
        var = commands.getoutput(busca)
        return var
 
#instancia o webservice para rodar na porta 8081 e responder para pesquisas como localhost
server = SOAPServer(('localhost',8081))
#registra a funcao que vamos chamar no nosso cliente
server.registerFunction(buscaLogs,'ns-buscalogs','buscaLogs')
#indica para o webservice nao parar de executar
server.serve_forever()

Acredito que já deixei tudo bem comentado de como funciona.

E segue o nosso cliente:

#!/usr/bin/python
#importa a biblioteca para fazer o webservice via SOAP
from SOAPpy import SOAPProxy
 
#parametro do webservice para ele saber a porta e o namespace (funcao) que vamos chamar
url = 'http://localhost:8081'
namespace = 'ns-buscalogs'
 
#conecta no webservice
server = SOAPProxy(url,namespace)
#isto para dar um dump das informacoes de entrada e saida
server.config.dumpSOAPOut = 1
server.config.dumpSOAPIn = 1
 
#escreve na tela o retorno da funcao do webservice buscaLogs
print server.buscaLogs('rafael','popper', '2010-03-16')

Os retornos ficaram mais ou menos desta forma:

[root@dsv cirolini]$ python cliente.py
*** Outgoing SOAP ******************************************************
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:buscaLogs xmlns:ns1="ns-buscalogs" SOAP-ENC:root="1">
<v1 xsi:type="xsd:string">rafael</v1>
<v2 xsi:type="xsd:string">popper</v2>
<v3 xsi:type="xsd:string">2010-03-16</v3>
</ns1:buscaLogs>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
************************************************************************
*** Incoming SOAP ******************************************************
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<buscaLogsResponse SOAP-ENC:root="1">
<Result xsi:type="xsd:string">Mar 16 08:46:19: end session - user rafael from 127.0.0.1: time=1 inbox=0/0 quit=1/4/138 stat=1/19/9 auth=1/150099/111 </Result>
</buscaLogsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
************************************************************************
Mar 16 08:46:19: end session - user rafael from 127.0.0.1: time=1 inbox=0/0 quit=1/4/138 stat=1/19/9 auth=1/150099/111

Tags | , ,

05

jan
2012

Sem Comentários

Em Blog
Python
Rails
Ruby

Por Allison

Como Ruby vem ganhando a prefrência do Python para administração de sistemas

Em 05, jan 2012 | Sem Comentários | Em Blog, Python, Rails, Ruby | Por Allison

Há cerca de três anos, eu comprei o livro “Python for Unix and Linux System Administration“, e ele me convenceu de que o Python seria a linguagem padrão de scripts para administradores do sistema Linux no futuro próximo. Eu estava trabalhando no projeto One Laptop Per Child (Um Laptop Por Criança) na época, onde o Python era a linguagem comum. Parecia que o Red Hat estava usando o Python para quase todo novo projeto. Além disso, o Unladen Swallow estava fazendo um progresso rápido. Eu não poderia estar mais errado.

Através de uma combinação de acidente histórico e algumas diferenças de recursos aparentemente pequenas, o Ruby está, inexoravelmente, se tornando a linguagem padrão dominante para a administração do sistema Linux.

Antes de eu ir mais adiante, seus administradores de sistema estão provavelmente gritando “Nós já temos uma linguagem de scripts, Bash!”. Mesmo amando Bash e commandlinefu, o Bash se torna uma obrigação, não uma qualidade quando seu script excede 100 linhas, e um completo pesadelo se você precisa analisar ou dar saída em HTML, CSV, XML, JSON etc.

Acidentes históricos

Em 2004, Luke Kanies falou sobre a construção do Puppet, um sistema de gerenciamento de configuração para melhorar as deficiências que ele via na CFEngine. Do excelente blog On Ruby:

Eu tentei implementar minha ideia em Perl, mas eu simplesmente não conseguia fazer os relacionamentos de classe funcionar (todos atributos e tipos de recursos precisavam ser classes, de acordo com o design na minha cabeça). Isso era quando o Python era o melhor, então eu naturalmente o experimentei, mas o Python simplesmente faz meus olhos sangrarem (e não, não era o espaço em branco, eram coisas como o fato de que `print` era uma instrução em vez de uma função, e `len` era uma função ao invés de um método).

Eu tinha um amigo que tinha ouvido falar que o Ruby era legal, mas não tinha de fato testado. Como eu estava simplesmente à toa na época, pensei em dar uma olhada. Quatro horas depois, nunca tendo visto uma linha de Ruby antes, eu tinha um protótipo funcional.

Dois anos depois, muitos desenvolvedores Puppet sentiram que o Puppet não satisfazia suas necessidades. Eles começaram suas própria ferramenta de configuração de gerenciamento, Chef, largamente inspirada pelo Puppet. A grande diferença entre Puppet e Chef é que Chef utiliza Ruby puro como “receita”, enquanto o Puppet usa sua própria linguagem de configuração baseada no Ruby.

Ambos Puppet e Chef estão sendo adotados rapidamente por grandes empresas, de acordo com a BusinessWeek. Se você ainda não estiver usando Puppet ou Chef, você deve começar a planejar a usá-los no futuro. Seja escolhendo Chef ou Puppet, você terá efetivamente o script da sua infraestrutura em Ruby. Depois de gastar 25% do seu tempo trabalhando com Puppet, você estará muito mais inclinado a escolher o Ruby para sua própria tarefa de scripting.

Projetos populares


Aqui está uma lista não definitiva de projetos sysadmin/devops em ruby

  • puppet
  • chef
  • vagrant
  • mcollective
  • cucumber (behavior-driven testing)
  • capistrano
  • rake (ruby Make)
  • aeolus project/openshift
  • cloud foundry
  • graylog2
  • logstash
  • travis-ci

e em Python:

  • openstack
  • cobbler
  • fabric
  • func
  • buildbot

O que é importante aqui não é o comprimento das respectivas listas, mas a importância dos projetos individuais. Chef, Puppet e Vagrant são os novos martelos e chaves-de-fenda da administração de sistemas. Se você for um administrador de sistema e ainda não está usando essas ferramentas, não se preocupe, você irá, cedo ou tarde.

O Openstack merece uma atenção especial, e é um projeto muito animador, mas ainda está no seu estágio inicial. Ele pode representar um papel importante no seu futuro como administrador do sistema.

Por favor, me notifique de projetos devops significantes que estão faltando nessa lista.

O que um administrador de sistema quer

Aqui estão os recursos em uma linguagem script que um administrador de sistema quer:

  • Um DSL para o problema do domínio
  • Alta produtividade, por exemplo, sintaxe expressiva e concisa
  • Fácil interação com comandos shell
  • Expressões regulares
  • Comandos de linha poderosos

Note que desempenho não está na lista de necessidades. O Ruby é acentuadamente mais lento que o Pyhton, e isso era particularmente verdade para a série 1.8.* do Matz Ruby Interpreter (MRI). No entanto, desempenho simplesmente não é algo crítico para 90% do nosso trabalho como administradores de sistema. Nós nos importamos mais com produtividade do que com desempenho. Legibilidade é legal, mas está um segundo distante de produtividade.

O Python não tem expressões regulares nativamente, provavelmente para melhorar a legibilidade. Ele também limita o número de variáveis globais de alto nível embutidas. De uma perspectiva de design de linguagem, isso é muito mais limpo. Da perspectiva do seu administrador de sistema padrão, treinado na rua, estressado, viciado em VI, é irritante.

Essas variáveis de alto nível também deixam as ONE_LINERS mais concisas. Aqui estão algumas.

$_ last line read from STDIN
$? last exit code from a child process
$stdin reference to stdin
$stderr reference to stdout
$stdout reference to stdout

Aqui está uma sessão IRB para mostrar esses valores em ação.

hitman@hiroko:~/pr$ irb
irb(main):001:0> %x[ ls -a ]
=> ".\n..\nfoo1\nfoo2\nfoo3\n"
irb(main):002:0> puts $?
0
=> nil
irb(main):003:0> %x[ ls xys ]
ls: cannot access xys: No such file or directory
=> ""
irb(main):004:0> puts $?
512
=> nil

“IRB?” você me zomba, “Você terá que arrancar o IPython das minhas mãos mortas geladas.”

Eu adoro o IPython. Eu usei o IPython por mais de quatro anos, e o IRB não tem comparação a ele. Dito isso, os atalhos do Ruby são bastante úteis, o suficiente pra compensar as deficiências do IRB. Existe algo chamado wirble que eu ainda não tentei, mas ele pode deixar o IRB bem mais produtivo.

Aqui está algum código Python para detectar se uma máquina é VMware VM.

# edit: fixed python code tks to kstrauser

import os
  if 'vmware' in os.popen('dmidecode').upper():
    print 'this is a vmware vm'
  else:
    print 'this is not a vmware vm'

Aqui está o mesmo código em Ruby:

`dmidecode`
if $_ ~= /vmware/i
   puts 'this is a vmware vm'
else
   puts 'this is not a vmware vm'

Francamente, esse tipo de código faz meus olhos sangrarem. O exemplo do Python é muito mais legível e mais fácil de manter. Dito isso, muitos dos administradores de sistema iriam gostar de quão conciso é o exemplo do Ruby.

Vamos ver como escrever comandos de linha em ambas as linguagens.

O código imprime as primeiras 10 linhas em um arquivo usando Python.

python -c "import sys; sys.stdout.write(''.join(sys.stdin.readlines()[:10]))" < /path/to/your/file

Aqui está o mesmo código em ruby:

ruby -ne 'puts $_ if $. <= 10 ' < /path/to/your/file

Compare você mesmo essas coleções de oneliners no Python e no Ruby. Se o Ruby te lembra o Perl, seus olhos não te enganam. Em muitas maneiras, ele é o filho querido do Perl e do Smalltalk.

DSLs FTW

Há algum tempo, eu tentei assistir a todas as palestras da Structure and Interpretation of Computing. Todo esse esforço falhou miseravelmente, mas eu me lembro de Harold Abelson dizendo que todo grande programa deveria ter seu próprio DSL interno adequado para o problema de espaço. Isso é debatível devido ao grande mundo da programação, mas eu acredito estar apto para a administração de sistemas. Passamos toda nossa carreira com vários DSLs diferentes. Cada formato de arquivo com configuração diferente é seu próprio DSL.

Se você comparar o rake (Ruby Make) ao código rails, eles parecem muito diferentes, quase linguagens diferentes. Se você comparar o código fabric a uma classe django, eles se parecem muito. Isso é uma força e uma responsabilidade. Não sou nenhum advogado de linguagem, mas parece muito mais fácil criar DSLs (Domain Specific Languages). O Ruby certamente gera DSLs com uma frequência muito maior do que o Python. Nenhuma ferramenta de construção do Python domina o problema de espaço como o rake faz na comunidade Ruby. A maioria dos projetos Python parece usar setup.py para tarefas administrativas, mesmo este não sendo seu objetivo explícito.

Ambos, Puppet e Chef são DSL para administração de sistemas. O Capistrano é uma DSL para deploy de aplicações. Sobrecarga de operadores e os blocos do Ruby facilitam a criação de DSLs.

Em resumo

A maior força do Ruby é sua grande flexibilidade. Existe muita “mágica” no Ruby, e às vezes essa magia é negra. O Python intencionalmente tem o mínimo de mágica. Sua maior força é a capacidade de reforçar as boas práticas através da sua comunidade. Essas práticas tornam o Python bastante legível em vários projetos. Eles garantem alta qualidade de documentação, fazendo sua biblioteca padrão arrasar. Mas o fato é que nós sysadmin precisamos de flexilidade mais do que precisamos de poder ou consistência. Mesmo assim, esses não são os motivos reais pelos quais o Ruby está tomando o lugar do Python.

O Ruby está rapidamente se tornando a linguagem de script para os administradores de sistema porque, em 2004, Luke Kanies olhou para o Python e se sentiu doente (eu tive a reação oposta). Como um administrador de sistema, você ou está ou logo estará usando o Puppet ou o Chef diariamente, gastando muito tempo essencialmente codificando em Ruby. Pessoalmente, eu prefiro em Python, mas estou mudando a escrita dos meus scripts para Ruby porque passo muito tempo no Chef.

Texto original em inglês de Bryan Berry, disponível em http://devopsanywhere.blogspot.com/2011/09/how-ruby-is-beating-python-in-battle.html

Fonte: Bryan Willson Berry/IMasters

Tags | , , , , , ,

27

dez
2011

Sem Comentários

Em Blog
Python

Por Allison

Threads em Python

Em 27, dez 2011 | Sem Comentários | Em Blog, Python | Por Allison

Python é uma linguagem bem genérica e fácil de ser utilizada. Qualquer usuário pode aprender a programar em Python de uma maneira bem fácil, principalmente porque a linguagem encapsula conceitos difíceis em implementações fáceis.

Neste artigo vamos tratar da utilização de threads em Python. Threads são fluxos de programas que executam em paralelo dentro de uma aplicação, isto é, uma ramificação de uma parte da aplicação que é executada de forma independente e escalonada independentemente do fluxo inicial da aplicação.

Imaginemos, por exemplo, uma aplicação que mede, de tempos em tempos, a condição de determinados sensores. Supondo que cada sensor precisa ser medido com uma frequência diferente, isto é, um a cada 30 segundos, outro a cada 45 segundos e, por fim, um terceiro a cada 75 segundos.

Implementar isto de maneira sequencial é trabalhoso. Um jeito fácil, porém, é a implementação de uma thread independente para a leitura em cada um dos sensores. Desta forma a thread espera o tempo determinado para a leitura do sensor a que ela está ligada, sem se preocupar, ou mesmo saber, sobre os outros sensores.

Assim, neste caso, bastaria fazer uma classe por tipo de sensor, sendo que cada classe seria uma thread. Para transformar uma classe em thread, são necessárias duas modificações na classe:

  • A classe em questão estender à classe Thread do pacote threading
  • Implementar o método run(), que será chamado quando a thread iniciar

Em Python, o pacote que providencia as funcionalidades de thread é chamado threading, e deve ser importado no começo do seu programa: from threading import Thread.

Segue um exemplo básico, de uma classe chamada Th que implementa Thread e o método run(). O conteúdo do método run será executado em uma thread separada sempre que o método start, definido na classe Thread e herdado pela classe Th no nosso exemplo, for chamado:

from threading import Thread
 
class Th(Thread):
 
def __init__ (self, num):
Thread.__init__(self)
self.num = num
 
def run(self):
 
print "Hello "
print self.num
 
 
a = Th(1)
a.start()
 

Apesar de, no exemplo acima, o conteúdo do método run ser executado em uma thread separada, não é possível comprovar isto apenas pela saída do programa.

Afim de comprovarmos que cada thread é executada de forma independente e escalonada independentemente do fluxo inicial da aplicação, vamos analisar o próximo exemplo. Nele criamos várias threads simples Th, como as do exemplo acima, porém ao invés de simplesmente imprimirmos uma mensagem na thread ela vai executar um número definido de vezes COUNTDOWN antes de finalizar sua execução:

from threading import Thread
import sys
 
COUNTDOWN = 5
 
class Th(Thread):
 
def __init__ (self, num):
sys.stdout.write("Making thread number " + str(num) + "n")
sys.stdout.flush()
Thread.__init__(self)
self.num = num
self.countdown = COUNTDOWN
 
def run(self):
 
while (self.countdown):
sys.stdout.write("Thread " + str(self.num) +
" (" + str(self.countdown) + ")n")
sys.stdout.flush()
self.countdown -= 1
 
 
for thread_number in range (5):
thread = Th(thread_number)
thread.start()

Uma das possíveis saídas para o programa acima é a seguinte:

Making thread number 0
Thread 0 (5)
Thread 0 (4)
Thread 0 (3)
Thread 0 (2)
Thread 0 (1)
Making thread number 1
Thread 1 (5)
Making thread number 2
Making thread number 3
Thread 2 (5)
Thread 1 (4)
Thread 1 (3)
Thread 2 (4)
Thread 1 (2)
Thread 2 (3)
Thread 1 (1)
Thread 2 (2)
Making thread number 4
Thread 2 (1)
Thread 3 (5)
Thread 4 (5)
Thread 4 (4)
Thread 3 (4)
Thread 4 (3)
Thread 3 (3)
Thread 4 (2)
Thread 3 (2)
Thread 4 (1)
Thread 3 (1)
 

Caso você rode o programa acima, a saída não necessariamente será igual a esta, já que a alocação das threads para execução no processador não é um processo determinado. Mesmo rodando múltiplas vezes, o mesmo programa em um mesmo computador as saídas irão variar de uma execução para outra.

Um ponto interessante de se notar no exemplo acima é que, em vez de usarmos print para imprimir na saída padrão, utilizamos sys.stdout.write seguido de uma chamada a sys.stdout.flush.

Isto foi feito para garantir que as mensagems fossem impressas em ordem, já que chamadas a print por diversas threads simultaneamente não garantem a ordem de impressão dos caracteres.

Sincronização de threads

Nos exemplos citados anteriormente, usou-se threads para efetuar processamento paralelos distintos e sem ligação entre si. No entanto, no mundo real muitas vezes as diversas linhas de execução representada pelas threads de um programa precisam, eventualmente, comunicar-se entre si.

Uma forma simples de comunicação é aquela que precisa ocorrer no final do processamento das threads. Exemplos típicos deste tipo de necessidade são programas que processam dados em vetores ou matrizes.

Se estes vetores ou matrizes forem muito grandes e os cálculos efetuados em cada elemento relativamente demorados e independentes até certo ponto, a utilização de threads pode acelerar bastante este tipo de cálculo, já que o trabalho é de alguma forma dividido entre as diversas threads.

Ao final do processo, basta realizarmos um cálculo mais simples que agrega os sub-totais calculados pelas threads. Este tipo de sincronização no final da execução de uma thread pode ser feito através do método join da classe Thread.

Por exemplo, imaginemos um programa que soma os valores de um vetor de inteiros com 1000 elementos cujos valores variam de 0 a 100. Para fins deste exemplo, este vetor será criado com valores randômicos.

Serão criadas quatro threads que calcularão a soma de 250 elementos cada uma. Ao fim do processamento, os sub-totais gerados pelas quatro threads serão somados para gerar um único valor total referente à soma de todos os elementos do vetor.

from threading import Thread
import random
import sys
 
NUM_VALUES = 1000
values = []
sequential_total = 0
threaded_total = 0
threads = []
NUM_THREADS = 4
 
class Th(Thread):
subtotal = 0
 
def __init__ (self, num):
sys.stdout.write("Making thread number " + str(num) + "n")
sys.stdout.flush()
Thread.__init__(self)
self.num = num
 
def run(self):
 
range_start = self.num * NUM_VALUES / NUM_THREADS
range_end = ((self.num + 1) * NUM_VALUES / NUM_THREADS) - 1
 
for i in range(range_start, range_end):
self.subtotal += values[i]
sys.stdout.write("Subtotal for thread " + str(self.num) +
": " + str(self.subtotal)
+ " (from " + str(range_start)
 
+ " to " + str(range_end) + ")n");
 
sys.stdout.flush()
 
def get_subtotal(self):
return self.subtotal
 
#### O programa comeca aqui #####
 
for i in range(NUM_VALUES):
values.append(random.randint(0,100))
 
for i in range(NUM_VALUES):
sequential_total += values[i]
 
print("Sequential total: " + str(sequential_total))
 
for thread_number in range(NUM_THREADS):
threads.insert(thread_number, Th(thread_number))
threads[thread_number].start()
 
for thread_number in range(NUM_THREADS):
threads[thread_number].join()
threaded_total += threads[thread_number].get_subtotal()
 
print("Threaded total: " + str(threaded_total))

Um exemplo de saída para o programa acima seria o seguinte:

Sequential total: 49313
Making thread number 0
Making thread number 1
Subtotal for thread 0: 12365 (from 0 to 249)
Making thread number 2
Subtotal for thread 1: 12568 (from 250 to 499)
Making thread number 3
Subtotal for thread 2: 11742 (from 500 to 749)
Subtotal for thread 3: 12638 (from 750 to 999)
Threaded total: 4931

Note que o valor total calculado pela versão sequencial da soma dos elementos do vetor é igual à soma calculada através das quatro threads criadas no programa para fazer o mesmo trabalho de forma dividida.

Uma outra forma de sincronização pode ser necessária quando há necessidade de se acessar variáveis cujos valores são compartilhados por várias threads rodando simultaneamente. Para isso, é necessário lançar mão da utilização de locks (threading.Lock) ou semáforos (threading.Semaphore). Um artigo a parte descreverá os locks e semáforos, é só aguardar pelo próximo post.

Conclusão

Este artigo fez uma introdução a programação com Thread em python, mostrando o que é uma thread, com deve ser construida classes que tornam-se thread e como sincronizar o fim das threads.

Recursos

artigo publicado originalmente no developerWorks Brasil, por Breno Leitão

Fonte: IMasters

Tags | , , ,

18

dez
2011

Sem Comentários

Em Blog
Python

Por Allison

Um guia para pacotes Python

Em 18, dez 2011 | Sem Comentários | Em Blog, Python | Por Allison

O principal para um projeto de software livre de sucesso é o pacote. Um ingrediente chave para um bom pacote é a versão. Como o projeto é de software livre, o ideal é publicar o pacote para aproveitar os muitos benefícios que a comunidade de software livre oferece.

Diferentes plataformas e linguagens têm mecanismos diversos para pacotes, mas este artigo se concentra especificamente em Python e seu ecossistema de pacotes. O artigo discute mecânica de pacotes para oferecer uma base e fornece exemplos práticos suficientes para que o leitor comece imediatamente.

Por que se preocupar com pacotes?

Além de ser a coisa certa a se fazer, há três motivos práticos para empacotar software:

  • Facilidade de uso
  • Estabilidade (com versões)
  • Distribuição

Tornar a instalação do aplicativo o mais simples possível é uma questão de consideração para com os usuários. Pacotes tornam o software mais acessível e mais fácil de instalar. Se for mais fácil de instalar, será mais fácil para que usuários comecem a usar o software.

Ao publicar um pacote no Python Package Index (PyPI), ele poderá ser facilmente acessado por meio de utilitários como pip oueasy_install.

Além disso, ao criar versões de pacotes, o desenvolvedor permite que os usuários “fixem” a dependência que seus projetos têm em relação ao software a uma versão em particular. Por exemplo, fixar Pinax para a versão 0.9a2.dev1017 seria expresso como:

Pinax==0.9a2.dev1017

Isso forçaria o projeto a usar o release 0.9a2.dev1017 do Pinax.

Versões garantem maior estabilidade caso sejam lançadas alterações de releases para seu software mais tarde que possam ter quebra de interfaces. Permitem aos usuários saber exatamente o que estão obtendo e facilitam para eles acompanhar as diferenças em releases.

Além disso, os desenvolvedores de projeto podem saber exatamente para o que estão criando código.

Um método comum para publicar pacotes no PyPI (ou em um servidor de distribuição próprio) é criar uma distribuição de origem para upload. Uma distribuição de origem é uma maneira padrão de empacotar a origem de seu projeto como uma unidade distribuível.

Há maneiras de criar distribuições binárias, mas, para o software livre, faz sentido também distribuir a origem.

Criar distribuições de origem facilita o uso de ferramentas que procuram o software na Internet, fazem download e instalam automaticamente. Esse processo ajuda não só no desenvolvimento local, mas também nas implementações do software.

Portanto, ao tornar mais fácil para usuários integrar e instalar o software, usar boas versões que permitem uma técnica confiável de fixação e publicar o pacote para distribuição mais ampla, você terá uma chance maior de que seu projeto tenha sucesso e obtenha aprovação ampla. Aprovação ampla pode levar a mais contribuidores — algo que certamente todo desenvolvedor de software livre deseja.

Anatomia de um arquivo setup.py

Um dos propósitos do script setup.py é servir como o executável que pode ser executado para empacotar o software e fazer upload para servidores de distribuição. O script setup.py pode variar bastante em conteúdo conforme você navega pelos repositórios Python populares. Este artigo se concentra no básico.

O arquivo setup.py pode ser usado para muitas tarefas diferentes, mas aqui você cria um que permitirá executar os seguintes comandos:

python setup.py register
python setup.py sdist upload

O primeiro comando, register, toma as informações fornecidas na função setup() no script setup.py e cria uma entrada no PyPI para seu pacote. Ele não faz upload; em vez disso, cria os metadados sobre o seu projeto, de modo que posteriormente seja possível fazer upload e hospedar os releases lá.

Os próximos dois comandos estão encadeados: sdist upload desenvolve uma distribuição de origem e faz upload dela para o PyPI. Mas há alguns pré-requisitos, como configurar seu próprio arquivo de configuração .pypirc e escrever efetivamente o conteúdo de setup.py.

Primeiro, configure o arquivo .pypirc. Ele deve estar em seu diretório inicial, que varia dependendo do sistema operacional. No UNIX®, Linux® e Mac OS X, chega-se lá digitando cd ~/. O conteúdo do arquivo deve conter suas credenciais PyPI, como mostrado abaixo:

[distutils]
index-servers =
pypi
 
[pypi]
username:xxxxxxxxxxxxx
password:xxxxxxxxxxxxx

Em seguida, acesse o PyPI e registre uma conta (não se preocupe, é grátis). Coloque o mesmo nome de usuário e senha criados no PyPI em seu arquivo .pypirc e nomeie o arquivo ~/.pypirc.

Agora, ao criar o script setup.py, é preciso decidir o que será exibido na página de índice do PyPI e qual será o nome do projeto. Comece copiando um modelo de setup.py que eu uso para projetos (consulte a listagem abaixo).

Ignorando as importações e funções, olhe para a parte inferior do modelo, para o que precisa ser mudado para se adequar ao seu projeto.

PACKAGE = ""
NAME = ""
DESCRIPTION = ""
AUTHOR = ""
AUTHOR_EMAIL = ""
URL = ""
VERSION = __import__(PACKAGE).__version__
 
setup(
name=NAME,
version=VERSION,
description=DESCRIPTION,
long_description=read("README.rst"),
author=AUTHOR,
author_email=AUTHOR_EMAIL,
license="BSD",
url=URL,
packages=find_packages(exclude=["tests.*", "tests"]),
package_data=find_package_data(
PACKAGE,
only_in_packages=False
),
classifiers=[
"Development Status :: 3 - Alpha",
"Environment :: Web Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Framework :: Django",
],
zip_safe=False,
)

Primeiro, observe que este modelo espera que o projeto tenha dois arquivos diferentes. O primeiro é usado para long_description: ele lê o conteúdo do arquivo README.rst que está no mesmo diretório que setup.py e passa o conteúdo como uma cadeia de caracteres para o parâmetro long_description.

Esse arquivo preenche a página de entrada no PyPI, portanto é bom descrever brevemente o projeto e mostrar exemplos de uso nesse arquivo. O segundo arquivo é __init__.py . Ele não é mencionado explicitamente aqui, mas a linha que define a variável VERSION importa o pacote; e quando isso acontece, Python precisa de um arquivo __init__.py e espera uma variável definida naquele módulo, chamada __version__.

Por enquanto, basta defini-la como uma cadeia de caractere:

# __init__.py
__version__ = "0.1"
 

Agora vamos ver as demais entradas:

Package é o pacote Python no projeto. É a pasta de nível superior que contém o módulo __init__.py que deve estar no mesmo diretório que o arquivo setup.py — por exemplo:

/-
|- README.rst
|- setup.py
|- dogs
|- __init__.py
|- catcher.py
 
Portanto,
 
dogs

seria o pacote aqui.

  • Name é geralmente o mesmo que Package ou semelhante, mas pode ser qualquer coisa. Name é como as pessoas chamarão o software, o nome pelo qual ele será listado no PyPI e — o mais importante — sob o qual os usuários irão instalá-lo (por exemplo, pip install NAME).
  • Description é apenas uma breve descrição do projeto. Uma frase é o bastante.
  • Author e Author_Email são o que parecem: o nome e endereço de e-mail do autor. Essas informações são opcionais, mas é uma boa prática fornecer um endereço de e-mail caso as pessoas queiram entrar em contato com você devido ao projeto.
  • URL é a URL do projeto. Essa URL pode ser o Web site do projeto, repositório Github ou qualquer URL que você queira. Novamente, essas informações são opcionais.

Pode ser útil também fornecer a licença e classificadores. Para mais informações sobre a criação de um arquivo setup.py, consulte a documentação do Python logo abaixo em Recursos.

Versão

Versões podem facilmente ser um tópico próprio, mas vale a pena mencioná-las no contexto de pacotes, pois um bom empacotamento envolve versões apropriadas. Versões são uma maneira de comunicar-se com o usuário: também permite que os usuários desenvolvam mais estabilidade e confiabilidade em seus aplicativos.

Com versões, o desenvolvedor diz aos usuários que ele mudou algo e dá limites explícitos para onde essas mudanças ocorreram.

Um padrão para versões em pacotes Python pode ser encontrado em Python Enhancement Proposal (PEP) 386. Essa proposta declara regras pragmáticas.

Mesmo que você não tenha lido e entendido a PEP, ou mesmo que não concorde com ela, seria sábio segui-la, pois mais e mais desenvolvedores Python estão acostumados a vê-la.

Além disso, versões não são apenas para releases estáveis transferidos por upload para PyPI, mas também são úteis para releases de desenvolvimento usando o sufixo devNN.

Geralmente não é bom fazer upload dessas versões de desenvolvedor para o PyPI, mas você ainda pode configurar seu próprio servidor de distribuição público (ou privado) para disponibilizá-las; dessa forma, usuários que queiram usar a versão mais recente podem citar isso em seu arquivo requirements.txt do pip. Aqui estão alguns exemplos de versões:

1.0.1        # 1.0.1 final release
1.0.2a       # 1.0.2 Alpha (for Alpha, after Dev releases)
1.0.2a.dev5  # 1.0.2 Alpha, Dev release #5

Publicação

As pessoas geralmente não irão localizar e instalar software que não tenha sido publicado. Na maioria das vezes, você deve publicar seus pacotes no PyPI.

Após configurar o arquivo de configuração .pypirc, o comando upload passado para setup.py transmite seu pacote para o PyPI. Geralmente isso é feito em conjunto com o desenvolvimento de uma distribuição de origem:

python setup.py sdist upload
 

Se você estiver usando seu próprio servidor de distribuição, inclua uma seção para autorização em seu arquivo .pypirc para esse novo local, e indique-o por nome ao fazer o upload:

python setup.py sdist upload -r mydist

Configurar seu próprio servidor de distribuição

A principal razão para usar um servidor de distribuição próprio em software livre é oferecer um lugar para publicar releases de desenvolvedor, pois o PyPI deve consistir apenas de releases estáveis. Por exemplo, você provavelmente quer que instale o release estável mais recente localizado no PyPI:

pip install MyPackage

No entanto, se posteriormente você instalar releases de desenvolvedor, esse comando acabará instalando o release mais recente, o que significa o de desenvolvedor.

É geralmente bom fixar um release, mas nem todos os usuários fazem isso. Portanto, garanta que o release estável mais recente seja sempre retornado se o usuário não especificar um número de versão.

Uma maneira de ter as vantagens tanto de um método (expor apenas releases estáveis para padrão do pip) como do outro (permitir que os usuários instalem pacotes de releases de desenvolvedor) é hospedar um servidor de distribuição próprio. O projeto Pinax faz isso para todos os seus releases de desenvolvedor.

O servidor de distribuição é apenas um índice, servido em Protocolo de Transporte de Hipertexto (HTTP), de arquivos no seu servidor. Deve ter a seguinte estrutura de arquivos:

 
/index-name/package-name/package-name-version.tar.gz

Dessa forma, é possível tornar o servidor privado configurando Basic-Auth no servidor da Web. É uma boa ideia incluir alguns recursos para upload de distribuições de origem também.

Para isso, é preciso incluir código para lidar com o upload, analisar o nome do arquivo e criar os caminhos de diretório para corresponder ao esquema acima. Essa estrutura existe para o projeto Pinax, que hospeda diversos repositórios.

pip e virtualenv

Embora este artigo tenha se concentrado primariamente na criação de pacotes, esta seção descreve o consumo de pacotes, oferecendo um pouco de apreciação para o que bons pacotes e versões fazem para os usuários.

pip é uma ferramenta que pode ser instalada diretamente, mas eu recomendo usá-la como parte de virtualenv. Recomendo usar virtualenv para tudo relacionado a Python, pois mantém os ambientes Python limpos.

Assim como uma máquina virtual permite executar diversos sistemas operacionais lado a lado, virtualenvs permitem executar diversos ambientes Python lado a lado. Eu não instalo nada no Python do meu sistema; em vez disso, crio um novo virtualenv para cada novo projeto ou utilitário em que trabalho.

Agora que você instalou virtualenv, pode brincar um pouco:

$ mkvirtualenv —no-site-packages testing
$ pip install Pinax
$ pip freeze|grep Pinax
$ pip uninstall Pinax
$ pip install —extra-index-url=http://dist.pinaxproject.com/fresh-start/
Pinax==0.9a2.dev1017
$ pip freeze|grep Pinax

Observe que a primeira instalação do pip foi transferida por download e instalada a partir do PyPI. pip freeze mostra todas as versões de pacotes instalados no virtualenv atual. pip uninstall faz exatamente o que você imagina: remove-se do virtualenv.

Em seguida, você instala uma versão de desenvolvedor do repositório novo para obter a versão de desenvolvimento do Pinax versão 0.9a2.dev1017.

Não é preciso ir para Web sites, fazer download de tarballs e criar links simbólicos para um pacote no site. (Era assim que eu costumava fazer, e causava muitos problemas.) Seus usuários obtêm tudo isso como resultado de bons pacotes, publicação e versões de seu projeto.

Conclusão

Em suma, vale muito a pena aprender a arte e ciência dos pacotes. Você obterá uma adoção maior dos usuários devido à facilidade de instalação e estabilidade que a divisão dos pacotes em versão oferece a eles.

Usando o modelo de setup.py fornecido abaixo em Recursos e discutido neste artigo, deve ser possível incluir pacotes em seu projeto rápido e facilmente. Comunicar-se com seus usuários por meio de versões apropriadas é uma questão de consideração com eles, pois facilita o rastreamento de mudanças entre releases.

Por fim, à medida que pip e virtualenv aumentam sua adoção, a confiança nos pacotes publicados — seja no PyPI ou em um servidor de distribuição próprio — aumenta. Portanto, não deixe de publicar os projetos que você quer compartilhar com o mundo.

Fonte: developerWorks Brasil/IMasters

Tags | , , ,