Planeta PythonBrasil

PythonBrasil[10]

May 25, 2016

Filipe Saraiva

if (LaKademy 2016) goto Rio de Janeiro

Rio de Janeiro, a “Cidade Maravilhosa”, terra do Verão eterno. Os raios de sol aqui são sempre claros e quentes, o mar é refrescante, a areia é confortável. As pessoas são alegres, o Rio de Janeiro tem boa música, comida, as festas mais loucas do mundo, e belos corpos se divertindo com jogos de praia.

Mas enquanto o Rio de Janeiro ferve, alguns contribuidores do KDE que vivem na América Latina estarão trabalhando juntos em uma sala fria e escura da cidade, participando do nosso sprint “multi-área” chamado Latin America Akademy – LaKademy 2016.

Nos meus planos pretendo fazer um monte de trabalho relacionado com o Cantor, incluindo uma pesada triagem de bugs e diversos testes com algumas tecnologias IPC. Eu gostaria de selecionar alguma delas para ser a tecnologia “oficial” de implementação de backends no Cantor. O programa precisa de uma tecnologia IPC com bom suporte multiplataforma para os principais sistemas operacionais desktop disponíveis. Estou pensando no DBus… mas você teria alguma sugestão ou dica para me dar?

Outros contribuidores também querem trabalhar no Cantor. Wagner quer compilar e testar a aplicação no Windows, além de iniciar a implementação de um backend para uma nova linguagem. Fernando, que foi meu aluno no SoK 2015, quer corrigir o backend para R. Estou muito feliz em ver estes desenvolvedores querendo sujar suas mãos no código-fonte do Cantor, portanto ajudarei-os em suas tarefas.

Durante o LaKademy também pretendo apresentar para os participantes algumas ideias e protótipos sobre 2 novos softwares que estou trabalhando. Espero pegar alguns feedbacks e em seguida pensar sobre os próximos passos para estas aplicações. Quem sabe eu possa submetê-las como novos projetos do KDE? Bem, veremos. 🙂

Espere por mais notícias de nossa fria e escura sala onde realizaremos o LaKademy no Rio de Janeiro. 🙂

por Filipe Saraiva em 25 de May de 2016 às 00:25

May 22, 2016

Programando Ciência

Complex scatter plots on Python [PART III] – Inserting labels into elements and defining more than one legend

Hey scientist! How is it going?

Do you remember our scatter plot based on IBGE [1] data? In the last post of this series we’ll make it even better, inserting labels indicating the states and defining two legends related to the regions and the population. Now the plot will be awesome! Let’s do this!

Do you remember the first two posts of this series ([2], [3])? From them we generated the following plot. The comments, as well as the previous posts, explain what each part of the code does, OK?

scatter_preliminaryII

The code necessary for generating this plot follows:

# importing necessary packages.
import matplotlib.pyplot as plt
import pandas as pd

# reading data_ibge.xls
data_brazil = pd.read_excel('data_ibge.xls', sheetname=2)

# color palette 5-class Dark2, from ColorBrewer2: http://colorbrewer2.org/
colors = ['#1b9e77',
          '#d95f02',
          '#7570b3',
          '#e7298a',
          '#66a61e']

# attribute_color() points to the color correspondent to each region.
def attribute_color(region):
    colors = {
        'North':'#1b9e77',
        'Northeast':'#d95f02',
        'Southeast':'#7570b3',
        'South':'#e7298a',
        'Central-West':'#66a61e'
    }
    return colors.get(region, 'black')

# creating the color vector.
color_region = list()
qty_states = len(data_brazil['Region'])

for state in range(qty_states):
    color_region.append(attribute_color(data_brazil['Region'][state]))

# generating the plot.
plt.scatter(x = data_brazil['LifeExpec'],
            y = data_brazil['GDPperCapita'],
            s = data_brazil['PopX1000'],
            c = color_region,
            alpha = 0.6)

plt.title('Brazilian development in 2013, according to each state', fontsize=22)
plt.xlabel('Life expectancy (years)', fontsize=22)
plt.ylabel('GDP per capita (R$)', fontsize=22)
plt.grid(True)

We’ll continue working on this code. Download data_ibge.xls [4] if you need.

First we’ll insert the abbreviation of each state in its respective circle. Let’s use the function text() from matplotlib [5] for that. The arguments are the text coordinates in the plot (x, y) and a string, which will be the exhibited text (s).

We’ll take advantage of the coordinates and abbreviations in data_brazil. Therefore:

  • For the first state, x = data_brazil[‘LifeExpec’][0], y = data_brazil[‘GDPperCapita’][0], and s = data_brazil[‘UF’][0];
  • For the second state, x = data_brazil[‘LifeExpec’][1], y = data_brazil[‘GDPperCapita’][1], and s = data_brazil[‘UF’][1], and so on.

Let’s put all in a for command, so we don’t need to repeat the instructions several times.

for state in range(len(data_brazil['UF'])):
    plt.text(x = data_brazil['LifeExpec'][state],
             y = data_brazil['GDPperCapita'][state],
             s = data_brazil['UF'][state],
             fontsize=16)

And done! Quick, right?

Now we will define the legends. The idea is to adapt a 2D object with the colors we defined, since the automatic legend won’t work in our example. For the first legend we define a vector indication the regions, in the order they appear on our table:

regions = ['North',
           'Northeast',
           'Southeast',
           'South',
           'Central-West']

Then we define the objects, based on [6]. Here we create a list and add elements with the colors within our vector colors. We need the package matplotlib.lines to create these elements. After creating the list, we use regions and legend1_line2d to generate a legend using the legend() function. Besides that arguments, we use several others; try to modificate them to see what they represent!

import matplotlib.lines as mlines

legend1_line2d = list()
for step in range(len(colors)):
    legend1_line2d.append(mlines.Line2D([0], [0],
                                        linestyle='none',
                                        marker='o',
                                        alpha=0.6,
                                        markersize=15,
                                        markerfacecolor=colors[step]))

legend1 = plt.legend(legend1_line2d,
                     regions,
                     numpoints=1,
                     fontsize=22,
                     loc='best',
                     shadow=True)

Let’s create the second legend. There are a bunch of advanced resources here. They appear commented on the code, so you can understand them better:

legend2_line2d = list()
legend2_line2d.append(mlines.Line2D([0], [0],
                                    linestyle='none',
                                    marker='o',
                                    alpha=0.6,
                                    markersize=np.sqrt(100),
                                    markerfacecolor='#D3D3D3'))
legend2_line2d.append(mlines.Line2D([0], [0],
                                    linestyle='none',
                                    marker='o',
                                    alpha=0.6,
                                    markersize=np.sqrt(1000),
                                    markerfacecolor='#D3D3D3'))
legend2_line2d.append(mlines.Line2D([0], [0],
                                    linestyle='none',
                                    marker='o',
                                    alpha=0.6,
                                    markersize=np.sqrt(10000),
                                    markerfacecolor='#D3D3D3'))

legend2 = plt.legend(legend2_line2d,
                     ['1', '10', '100'],
                     title='Population (in 100,000)',
                     numpoints=1,
                     fontsize=20,
                     loc='upper left',
                     frameon=False,  # no edges
                     labelspacing=3, # increase spacing between labels
                     handlelength=5, # increase spacing between objects and text
                     borderpad=4     # increase the margins of the legend
                    )
plt.gca().add_artist(legend1)

plt.setp(legend2.get_title(),fontsize=22)  # increasing the legend font

Finally, we use plt.show() to show the plot. Check the result:

scatter_finalENUS.jpg

In this post series we employed several Python elements, from the basics to some advanced ones. Now we can look to the plot and have more information than only seeing the table, isn’t it? The complete code for this plot is available [7]! Check it out!

Did you like the result? What would you do different? Do you have ideas on data to study? Write them to us in the comments!

Thanks scientist! Gigaregards, see you next time!


Did you like this post? Please comment and share with your friends!
Want to download Programando Ciência codes? Go to our GitHub!
Make a donation for Programando Ciência!
Like us also on Facebook: www.facebook.com/programandociencia
I’m on Twitter! Follow me if you can! @alexdesiqueira


por alexandrejaguar em 22 de May de 2016 às 18:32

Gráficos de dispersão complexos no Python [PARTE III] – Inserindo rótulos em elementos e definindo mais de uma legenda

Faaaaaaaaaaaaala cientista! Tudo certo?
Lembra do nosso gráfico de dispersão baseado nos dados do IBGE [1]? No último post dessa série vamos deixá-lo ainda melhor. Colocaremos rótulos indicando qual é cada estado, e definiremos duas legendas, relacionadas às regiões e à população. Assim o gráfico ficará bem completo! Vamos lá?

Lembra dos primeiros posts da série ([2], [3])? A partir deles geramos o gráfico a seguir. Os comentários, assim como os posts anteriores, explicam o que cada parte do código faz, OK?

scatter_preliminarII

Veja o código necessário para gerar esse gráfico a seguir:

# importando os pacotes necessários.
import matplotlib.pyplot as plt
import pandas as pd

# lendo o arquivo dados_ibge.xls
dados_brasil = pd.read_excel('dados_ibge.xls', sheetname=2)

# paleta de cores 5-class Dark2, do ColorBrewer2: http://colorbrewer2.org/
cores = ['#1b9e77',
         '#d95f02',
         '#7570b3',
         '#e7298a',
         '#66a61e']

# a função atribui_cor() aponta a cor correspondente a cada região.
def atribui_cor(regiao):
    cores = {
        'Norte':'#1b9e77',
        'Nordeste':'#d95f02',
        'Sudeste':'#7570b3',
        'Sul':'#e7298a',
        'CentroOeste':'#66a61e'
    }
    return cores.get(regiao, 'black')

# criando o vetor de cores.
cor_regiao = list()
qtde_estados = len(dados_brasil['Regiao'])

for estado in range(qtde_estados):
    cor_regiao.append(atribui_cor(dados_brasil['Regiao'][estado]))

# gerando o gráfico.
plt.figure(figsize=(24,16))
plt.scatter(x = dados_brasil['ExpecVida'],
            y = dados_brasil['PIBperCapita'],
            s = dados_brasil['PopX1000'],
            c = cor_regiao,
            alpha = 0.6)
plt.title('Desenvolvimento do Brasil em 2013, por estado', fontsize=22)
plt.xlabel('Expectativa de vida (anos)', fontsize=22)
plt.ylabel('PIB per capita (R$)', fontsize=22)
plt.grid(True)

Continuaremos trabalhando com esse código. Caso precise, baixe o arquivo dados_ibge.xls [4].

Primeiro vamos inserir a sigla dos estados em cada círculo. Usaremos a função text() do matplotlib [5] para colocar os rótulos em cada estado. Os argumentos são as coordenadas do texto na figura (x, y) e uma string, que será o texto exibido (s).

Para que o valor seja facilmente localizado em cima de cada círculo, aproveitaremos as coordenadas e as siglas dadas em dados_brasil. Assim:

  • Para o primeiro estado, x = dados_brasil[‘ExpecVida’][0], y = dados_brasil[‘PIBperCapita’][0] e s = dados_brasil[‘UF’][0];
  • Para o segundo estado, x = dados_brasil[‘ExpecVida’][1], y = dados_brasil[‘PIBperCapita’][1] e s = dados_brasil[‘UF’][1], e assim por diante.

Colocaremos tudo em um comando for, para que não precisemos repetir as instruções várias vezes.

for estado in range(len(dados_brasil['UF'])):
    plt.text(x = dados_brasil['ExpecVida'][estado],
             y = dados_brasil['PIBperCapita'][estado],
             s = dados_brasil['UF'][estado],
             fontsize=16)

E pronto! Rápido, não é mesmo?

Agora colocaremos as legendas. A ideia é adaptar um objeto 2D com as cores que definimos, já que a legenda automática não funcionará nesse caso. Para a primeira legenda definiremos um vetor indicando as regiões, na ordem em que aparecem na nossa tabela:

regioes = ['Norte',
           'Nordeste',
           'Sudeste',
           'Sul',
           'Centro-Oeste']

Definimos então os objetos baseados na ideia de [6]. Aqui criamos uma lista e adicionamos elementos com as cores presentes no nosso vetor cores. Para criarmos esses elementos, precisaremos do pacote matplotlib.lines. Após criar a lista, utilizamos regioes e legend1_line2d para gerarmos uma legenda com a função legend(). Há vários argumentos além desses; tente modificá-los para ver o que eles representam!

import matplotlib.lines as mlines

legend1_line2d = list()
for passo in range(len(cores)):
    legend1_line2d.append(mlines.Line2D([0], [0], linestyle="none",
                                       marker="o", alpha=0.6, markersize=15,
                                       markerfacecolor=cores[passo]))

legend1 = plt.legend(legend1_line2d, regioes, numpoints=1, fontsize=22, loc="best", shadow=True)

Vamos então à segunda legenda. A criação da lista é parecida, mas há vários recursos avançados aqui. Eles aparecem comentados no código, para que você entenda melhor:

legend2_line2d = list()
legend2_line2d.append(mlines.Line2D([0], [0], linestyle="none",
                                       marker="o", alpha=0.6, markersize=np.sqrt(100),
                                       markerfacecolor='#D3D3D3'))
legend2_line2d.append(mlines.Line2D([0], [0], linestyle="none",
                                       marker="o", alpha=0.6, markersize=np.sqrt(1000),
                                       markerfacecolor='#D3D3D3'))
legend2_line2d.append(mlines.Line2D([0], [0], linestyle="none",
                                       marker="o", alpha=0.6, markersize=np.sqrt(10000),
                                       markerfacecolor='#D3D3D3'))

legend2 = plt.legend(legend2_line2d,
                     ['1', '10', '100'],
                     title='População (em 100.000)',
                     numpoints=1,
                     fontsize=20,
                     loc="upper left",
                     frameon=False,  # sem bordas
                     labelspacing=3, # aumenta o espaçamento entre os rótulos
                     handlelength=5, # aumenta o espaçamento entre o objeto e o texto
                     borderpad=4     # aumenta a borda da legenda
                    )
plt.gca().add_artist(legend1)

plt.setp(legend2.get_title(),fontsize=22)  # aumentando o tamanho da fonte da legenda

Por fim, usamos plt.show() para exibir o gráfico. Veja o resultado a seguir:

scatter_finalPTBR.jpg

Nessa série de posts empregamos vários elementos da linguagem Python, desde os mais básicos até alguns avançados. Agora conseguimos olhar para o gráfico e ter mais informações do que apenas ver uma tabela, não é mesmo? O código completo para esse gráfico está disponível [7]! Corre lá baixar e estudar!

Gostou do resultado? O que você faria diferente? Tem mais ideias sobre dados a estudar? Escreva pra gente nos comentários!

Valeu cientista! Um giga abraço e até a próxima!


Gostou? Curta e compartilhe com seus amigos!
Quer baixar os códigos do Programando Ciência? Corre lá no nosso GitHub!
Faça uma doação pro Programando Ciência!
Curta a gente também no Facebook: www.facebook.com/programandociencia
Estou no Twitter! Siga-me se puder! @alexdesiqueira

 


por alexandrejaguar em 22 de May de 2016 às 15:46

Lauro Moura

matplotlib.pyplot em (menos de) 5 minutos

import matplotlib.pyplot as plt

x_data = [1,2,4,5,6,7]
y_data = [x**2 for x in x_data]

# Cria um gráfico de linha e retorna o "artista" para detalhar aquela
# série de dados
line, = plt.plot(x_data, y_data)

# Adiciona uma descrição à série acima
line.set_label('Wowness vs doge count')

# As duas operações acima podem ser repetidas para criar novas séries de
# dados.

# Mostra a caixa de legenda
plt.legend()

# Ao invés de um fundo branco, mostra as linhas de 'grade'
plt.grid()

# Descrição dos eixos
plt.xlabel("Number of doges")
plt.ylabel("Wow, how wowsome am i?")

# Título do gráfico
plt.title("Wow, so much graph!")

# Salva o gráfico num arquivo
plt.savefig('doge_graph.png')

# Mostra uma janela com o gráfico
plt.show()

# Fecha a 'janela' virtual, destruindo o gráfico e liberando a memória
plt.close()

Resultado:

doge_graph

 

PS: Se receber um erro “SyntaxError: Non-ASCII character…” rodando o script acima, eu diria que a solução recomendada é mudar para o python 3:)


por lauro em 22 de May de 2016 às 11:16

May 21, 2016

Kodumaro

Aspectos – parte I

Atualizado no blog novo.

Um paradigma muito útil é a Programação orientada a Aspectos.

Consiste em separar e encapsular as funcionalidades de um código conforme sua importância.

Nesta primeira parte, abordaremos de forma simples tal separação e deixaremos o conceito de mixins para a parte II.

Vamos começar com um exemplo: imagine uma view que modifica o estado de um objeto, retornando um hash do novo estado:
@app.route('/people/<uuid>/', methods=['PATCH'])
def update_person(uuid):
person = db.person.find({ '_id': uuid }).first()
if not person:
raise Http404

try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }

person.update(data)
db.person.save(person)

r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)

return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }

A solução atende, mas é de difícil manutenção. Perceba que a função chamada update_person (atualiza pessoa) faz muito mais do que simplesmente atualizar os dados:
  • Recupera o documento do banco, retornando 404 se não existir;
  • Faz os parsing dos dados recebidos, retornando 400 em caso de erro;
  • Efetivamente atualiza o documento;
  • Serializa o objeto para a resposta;
  • Gera um hash da serialização;
  • Responde a requisição com formato conveniente.

Cada um desses passos é um aspecto do processo e pode ser isolado do restante.

Vamos então separar o primeiro aspecto: recuperação do documento.
def retrieve_person_aspect(view):
@wraps(view)
def wrapper(uuid):
person = db.person.find({ '_id': uuid }).first()
if not person:
raise Http404

return view(person)
return wrapper

@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
def update_person(person):
try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }

person.update(data)
db.person.save(person)

r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)

return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }

Agora a recuperação do documento está isolada, podendo inclusive ser usada em outras views. Nossa view já recebe o documento recuperado e não precisa lidar com o fato dele existir ou não.

Porém ainda temos muita coisa misturada. Por exemplo, a obtenção e parsing dos dados recebidos: isso caracteriza outro aspecto do código, que não a atualização do documento.

Podemos portanto, separá-los:
def parse_data_aspect(view):
@wraps(view)
def wrapper(person):
try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }

return view(person, data)
return wrapper

def retrieve_person_aspect(view):
...

@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)

r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)

return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }

A função update_person já está muito mais limpa: atualiza o documento, serializa e retorna o hash, mas ainda faz coisas demais. Vamos separar o tratamento do retorno:
def respond_etag_aspect(view):
@wraps(view)
def wrapper(person, data):
response = view(person, data)
return json.dumps({ 'etag': md5(response).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }
return wrapper

def parse_data_aspect(view):
...

def retrieve_person_aspect(view):
...

@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
@respond_etag_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)

r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
return ';'.join('{}:{}'.format(k, v) for k, v in r)

As coisas estão ficando cada vez mais separadas. A única coisa que a função update_person faz agora além de atualizar o documento é serializá-lo. Isso também pode ser isolado:
def serialize_person_aspect(view):
@wraps(view)
def wrapper(person, data):
response = view(person, data)
r = [(str(k), repr(v)) for k, v in response.iteritems()]
r.sort()
return ';'.join('{}:{}'.format(k,v) for k, v in r)

def respond_etag_aspect(view):
...

def parse_data_aspect(view):
...

def retrieve_person_aspect(view):
...

@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
@respond_etag_aspect
@serialize_person_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)
return person

Perceba que, com a separação dos aspectos em funções distintas, o código ficou muito mais semântico:
  • retrive_person_aspect apenas recupera o documento do banco;
  • parse_data_aspect apenas faz o parsing dos dados recebidos;
  • respond_etag_aspect apenas gera o formato correto da resposta;
  • serialize_person_aspect apenas serializa o documento;
  • finalmente, update_person apenas atualiza o documento.

Observações

  • db é um objeto de banco de dados MongoDB, apenas para fim de exemplo.
  • app é uma aplicação Flask, apenas para fim de exemplo.
  • A ordem dos decoradores é importante.
  • Os imports foram omitidos:
import json
from functools import wraps
from hashlib import md5

Na parte II abordaremos mixins.

[]’s
Cacilhας, La Batalema

por ℭacilhας, ℒa ℬatalema (noreply@blogger.com) em 21 de May de 2016 às 12:20

PythonClub

Python com Unittest, Travis CI, Coveralls e Landscape (Parte 4 de 4)

Fala pessoal, tudo bem?

Na terceira parte deste tutorial, aprendemos a usar o Coveralls para gerar relatórios de testes para o nosso projeto. A próxima ferramenta que iremos estudar será o serviço Landscape. Neste tutorial serei breve, já que o uso default da ferramenta é bem simples.

Sobre o Landscape

Landscape é uma ferramenta online semelhante ao já conhecido PyLint, ou seja, é um verificador de bugs, estilo e de qualidade de código para Python.

Quando ativamos a análise do Landscape em nosso repositório, ele é executado após cada push ou pull request e realiza uma varredura em nosso código fonte Python atrás de possíveis bugs, como por exemplo variáveis sendo usadas antes de serem declaradas, nomes reservados sendo usados como nomes de variáveis e etc. Ele também verifica se a formatação do seu código esta seguindo a PEP8 e aponta possíveis falhas de design em seu código.

Uma vez que a análise esteja finalizada, a ferramenta indica em porcentagem a "qualidade" do nosso código, ou em palavras mais precisas, o quanto nosso código está bem escrito segundo as boas práticas de desenvolvimento. Vale deixar claro que o Landscape não verifica se seu código funciona corretamente, isso é responsabilidade dos testes que você escreveu, como foi visto na primeira parte do tutorial.

Semelhante as ferramentas dos tutoriais anteriores, o Landscape é totalmente gratuito para projetos opensource.

Criando uma conta

O processo de inscrição é simples. No topo da página temos a permissão de nos inscrevermos usando a conta do Github. Realize a inscrição e vamos as configurações.

Ativando o serviço

De todas as ferramentas apresentadas, esta é a mais simples de configurar. O único passo necessário aqui é ativar o serviço para o nosso repositório. Como exemplo, estarei usando o mesmo repositório dos últimos tutoriais. Clique aqui para visualizar o repositório.

Assim que realizar o cadastro, vamos nos deparar com uma tela contendo a listagem dos nosso repositórios que estão utilizando o serviço. Se você nunca usou o serviço provavelmente não terá nenhum repositório, então faça o seguinte: clique no botão Sync with Github now, para realizar a sincronização com a sua conta do Github. Assim que a sincronização estiver completa, clique no botão Add repository.

Ao clicar, seremos levados a uma tela com a listagem de todos os repositórios que temos permissão de escrita. Procure o repositório que deseja ativar o serviço (lembrando que o Landscape funciona apenas para projetos Python) e o selecione (basta clicar sobre o nome do repositório).

Adicione o repositório clicando no botão verde Add Repository, logo abaixo da lista. Seremos novamente redirecionados a tela inicial, agora com o repositório escolhido já visível.

Inclusive, a partir desse momento, o Coveralls já irá iniciar a análise do seu projeto. Clique no nome do repositório para ver mais detalhes da analise.

No caso do meu projeto de teste, temos que a "saúde" do código está em 100%, ou seja, nenhuma parte do código apresenta erros de estilo, bugs e está utilizando boas práticas de programação em todo seu escopo.

Na barra lateral localizada à esquerda da página, temos alguns items, entre os quais os mais importantes são descritos a seguir:

  • Error: são instruções no código que provavelmente indicam um erro. Por exemplo, quando referenciamos uma variável sem declará-la antes ou realizamos a chamada de algum método inexistente.
  • Smells: são sinais ou sintomas no código que possivelmente indicam uma falha no projeto do software. Diferentemente de um bug, code smells não indicam uso incorreto da linguagem de programação e nem impedem o software de funcionar. Ao invés disso, eles indicam falhas no design do projeto que podem atrasar seu desenvolvimento ou mesmo ser a porta de entrada para bugs no futuro. Exemplos de code smells são: métodos ou códigos duplicados, classes muito grandes, uso forçado de algum design pattern quando o mesmo poderia ser substituído por um código mais simples e fácil de manter, métodos muito longos ou com excessivo números de parâmetros e por aí vai. A lista pode crescer muito haha... para mais detalhes leia.
  • Style: como o nome sugere, este item exibe os erros de estilo em seu código indicando trechos de código que não estão seguindo as regras de estilo da PEP8, trechos de códigos com identação incorreta e etc.

Como último passo, agora somente nos resta adicionar uma badge no arquivo README.md em nosso repositório. Assim poderemos ver a porcentagem de "saúde" do nosso projeto sem precisar acessar a página do Landscape.

Na página com o resultado da análise (onde é exibido a porcentagem de "saúde" do seu projeto), podemos pegar a badge do Landscape. No canto superior direito da tela, você encontra os botões abaixo:

Clique na badge (onde está escrito health) e a seguinte janela será exibida:

Selecione o texto da opção Markdown e cole-o no README.md do seu repositório. O meu README.md ficou assim:

# Codigo Avulso Test Tutorial
[![Build Status](https://travis-ci.org/mstuttgart/codigo-avulso-test-tutorial.svg?branch=master)](https://travis-ci.org/mstuttgart/codigo-avulso-test-tutorial)

[![Coverage Status](https://coveralls.io/repos/github/mstuttgart/codigo-avulso-test-tutorial/badge.svg?branch=master)](https://coveralls.io/github/mstuttgart/codigo-avulso-test-tutorial?branch=master)

[![Code Health](https://landscape.io/github/mstuttgart/codigo-avulso-test-tutorial/master/landscape.svg?style=flat)](https://landscape.io/github/mstuttgart/codigo-avulso-test-tutorial/master)

Também é possível configurar o Landscape para que o mesmo exclua algum diretório/arquivo da análise (muito útil com arquivos de interface compilados, usando por quem trabalha com PyQt/PySide) entre outras opções, mas isso fica para um tutorial futuro.

Abaixo podemos ver as três badges que adicionamos em nosso projeto. Clique aqui para acessar o repositório.

Conclusão

Pronto pessoal, agora temos o nosso repositório exibindo informações sobre os testes unitários, relatórios de testes e analises de qualidade de código. Isso não garante que seu projeto seja livre de falhas e bugs, mas te ajuda a evitá-los.

Vale lembrar que todas essas ferramentas ajudam muito, mas nada substitui o senso crítico e o hábito de sempre usar boas práticas durante o desenvolvimento. Por isso sempre busque aprender mais, estudar mais, ser humilde e ouvir quem tem mais experiência que você. Enfim, ser um programador e uma pessoa melhor a cada dia. Fica o conselho para todos nós, incluindo para este que vos escreve.

Espero que tenham gostado desta série de tutoriais. Obrigado por ler até aqui e até o próximo post.

Publicado originalmente: python-com-unittest-travis-ci-coveralls-e-landscape-parte-4-de-4

por Michell Stuttgart em 21 de May de 2016 às 00:09

May 18, 2016

Magnun Leno

Instalando o Django 1.9 com PostgreSQL para Desenvolvimento

Eu sempre me foquei em frameworks mais simples (Web2py) ou em microframeworks (Flask), por achar que "full stack frameworks" poderiam adicionar um overhead muito grande, tanto na execução do sistema quando no meu fluxo de desenvolvimento.

Django Name

Porém, no segundo semestre do ano passado, a comunidade do GrupyDF fez uma compra ...

Instalando o Django 1.9 com PostgreSQL para Desenvolvimento é um artigo original de Mind Bending

por Magnun em 18 de May de 2016 às 14:31

May 17, 2016

Aprenda Python

Visualizadores de arquivo reStructuredText

Os arquivos [reStructuredText](http://docutils.sourceforge.net/rst.html) (ou `rst`) são muito usados para escrever documentação técnica. Principalmente para aqueles projetos que têm a documentação hospedada no site [Read The Docs](http://readthedocs.org) ou que estão disponíveis no [pypi](http://pypi.python.org). O [Sphinx](http://www.sphinx-doc.org/en/stable/) é um projeto bem conhecido para

por Vinicius Assef (noreply@blogger.com) em 17 de May de 2016 às 17:28

Magnun Leno

Hack ‘n’ Cast v0.20 - Bitcoin: Mineração e Carteiras

Bitcoins. O que são, como conseguir, onde gastar? E o que são as carteiras? Descubra hoje, no Hacker Reporter!

Baixe o episódio e leia o shownotes

por Magnun em 17 de May de 2016 às 04:46

May 16, 2016

Aprenda Python

3 ways to assign value to a variable if it is empty in bash

Despite how many years I work with shell script in bash, each day I learn a bit more about it. Let's see 3 examples of assigning a value to a variable if it is empty: if test -z "$VAR" then VAR="some value" fi It's the same as: if test ! "$VAR" then VAR="some value" fi It's also the same thing as: : ${VAR="some value"} Of course each approach

por Vinicius Assef (noreply@blogger.com) em 16 de May de 2016 às 22:13

May 14, 2016

Thiago Avelino

"Porque escrever sobre obesidade?" in Hackeando a Obesidade

Exite muitas coisas que leva uma pessoa ficar com sobre peso, não estou aqui para falar sobre elas e sim tentar ajudar pessoas que estão…

por Avelino em 14 de May de 2016 às 18:23

JungleCoders

Convertendo um jogo escrito em Basic para Python - Parte I

A nostalgia dos computadores da década de 80 é algo que nunca parei de ter. Quando criança, tive a sorte de utilzar vários computadores de 8-bits, como ZX-81, ZX-Spectrum, Apple II e MSX, ou melhor, seus clones nacionais (TK-85, TK-90X, TK2000), uma vez que o Brasil vivia a época da Reserva do Mercado de Informática.
Numa época que não havia Internet, nós passávamos o tempo a digitar programas. Uma série de livros sobre programação de jogos foi editada pela editora Lutécia no Brasil, mas os originais americanos foram liberados pela Usborne. Neste artigo, eu vou traduzir o jogo principal do Computer Battlegames, chamado de Missile, de Apple II Basic para Python com Pyglet. A listagem original está na página 34 do livro em inglês (ver pdf acima).

10 HOME
20 HGR
30 HCOLOR=3
40 DIM Y(3),F(3)
50 N=1 : MS=5
60 PS=INT(RND(1)*6+4)
70 P=INT(RND(1)*135+11)
80 GOSUB 400
90 FOR I=PS TO 265 STEP PS
100 X=I-PS : Y=159-P : C=0 : GOSUB 300
110 X=I : C=3: GOSUB 300
120 F$="" : IF PEEK(-16384)>127 THEN GET F$
130 IF F$="" OR N>3 THEN 160
140 F(N)=1
150 N=N+1
160 FOR J=1 TO 3
170 C=0 : GOSUB 350
180 IF F(J)=0 OR Y(J)>145 THEN 230
190 Y(J)=Y(J)+MS
200 C=3 : GOSUB 350
210 X=J*70-I : Y=P-Y(J)
220 IF X>-1 AND X<15 AND Y>-9 AND Y<5 THEN 270
230 NEXT
240 NEXT
250 VTAB 22 : PRINT "MISSED"
260 END
270 VTAB 22 : PRINT "HIT!!!"
280 END
300 HCOLOR=C
310 HPLOT X,Y TO X,Y-8
320 HPLOT TO X+3,Y-2 : HPLOT TO X+12, Y-2
330 HPLOT TO X+14,Y : HPLOT TO X,Y
340 RETURN
350 HCOLOR=C
360 HPLOT 70*J,158-Y(J) TO 70*J,154-Y(J)
340 RETURN
350 HCOLOR=C
360 HPLOT 70*J, 158-Y(J) TO 70*J,154-Y(J)
370 RETURN
400 FOR J=1 TO 3
410 HPLOT 70*J-5,159 TO 70*J+5,159
420 NEXT
430 RETURN
Vejamos o jogo rodando em um emulador:

Versão comentada:
# Limpa a tela
10 HOME
# Entra no modo de alta resolução 280x192
20 HGR
# Seleciona a cor 3 (purpura/magenta)
30 HCOLOR=3
# Cria dois vetores com 3 elementos cada
40 DIM Y(3),F(3)
# Inicializa N igual a 1 e MS igual a 5
50 N=1 : MS=5
# Gera um número aleatório entre 0 e 6 + 4
60 PS=INT(RND(1)*6+4)
# Gera um número aleatório entre 0 e 135 + 11
70 P=INT(RND(1)*135+11)
# Desvia para subrotina
80 GOSUB 400
# Loop: repete I de PS até 265, incrementando de PS
90 FOR I=PS TO 265 STEP PS
# Calcula os valores de X, Y e C. Desvia para subrotina em 300
100 X=I-PS : Y=159-P : C=0 : GOSUB 300
# Define X e C. Desvia para subrotina 300
110 X=I : C=3: GOSUB 300
# Verifica se uma tecla foi pressionada. Se foi, guarda em F
120 F$="" : IF PEEK(-16384)>127 THEN GET F$
# Se algo foi pressionado ou se N>3 desvia para 160
130 IF F$="" OR N>3 THEN 160
# F[N] = 1
140 F(N)=1
# N+=1
150 N=N+1
# Repete J de 1 até 3
160 FOR J=1 TO 3
# Zera C e desvia para subrotina da linha 350
170 C=0 : GOSUB 350
# Se F[J]==0 ou Y[J]>145 desvia para 230
180 IF F(J)=0 OR Y(J)>145 THEN 230
# Y[J]+=MS
190 Y(J)=Y(J)+MS
# C=3. Desvia para subrotina da linha 350
200 C=3 : GOSUB 350
# Calcula X e Y
210 X=J*70-I : Y=P-Y(J)
# Se X>-1 e X<15 e Y>-9 e Y<5 desvia para 270
220 IF X>-1 AND X<15 AND Y>-9 AND Y<5 THEN 270
# Fim do loop, volta para o for da linha 160
230 NEXT
# Fim do loop, volta para for da linha 90
240 NEXT
# Posiciona o cursor na linha 22 e imprime MISSED
250 VTAB 22 : PRINT "MISSED"
# Termina o programa
260 END
# Posiciona o cursor na linha 22 e imprime HIT!!!
270 VTAB 22 : PRINT "HIT!!!"
# Termina o programa
280 END
# Troca a cor de desenho para C
300 HCOLOR=C
# Traça uma linha de X,Y até X,Y-8
310 HPLOT X,Y TO X,Y-8
# Continua a linha
320 HPLOT TO X+3,Y-2 : HPLOT TO X+12, Y-2
330 HPLOT TO X+14,Y : HPLOT TO X,Y
# Retorno da subrotina
340 RETURN
# Troca a cor de desenho para C
350 HCOLOR=C
# Desenha linha
360 HPLOT 70*J,158-Y(J) TO 70*J,154-Y(J)
# Volta da subrotina
340 RETURN
# Troca a cor para C
350 HCOLOR=C
# Desenha linha
360 HPLOT 70*J, 158-Y(J) TO 70*J,154-Y(J)
# Volta da subrotina
370 RETURN
# Repete J de 1 à 3
400 FOR J=1 TO 3
# Desenha linha
410 HPLOT 70*J-5,159 TO 70*J+5,159
# Fim do loop, volta para linha 400
420 NEXT
# Retorna da subrotina
430 RETURN
Bom, como precisaremos desenhar, vamos instalar a Pyglet:
pip3 install pyglet

A primeira coisa a fazer é criar a janela da Pyglet e separar a janela do jogo em si. No caso, a janela é responsável por receber os eventos do teclado. A configuração do OpenGL também precisam ser feitas aqui. Como a alta resolução do Apple II é muito pequena 280x192 pontos, eu estou usando uma escala 4. Desta forma, as coordenadas permanecerão as mesmas, mas os gráficos serão 4 vezes maiores. Se ficar muito grande em seu monitor, você pode ajustar a escala.
Eu tentei manter os comentários originais para ficar mais fácil de relacionar o código novo com o antigo.
Outra mudança são as coordenadas Y. No Apple II, Y = 0 é a primeira linha e em OpenGL é a última.
Vejamos como ficou o código da janela:
class Missile(pyglet.window.Window):
# 20 HGR
def __init__(self):
self.scale = 4 # Escala os gráficos 4x
self.frames = 8 # Frames por segundo.
# Define quantas vezes vamos atualizar a tela por segundo
super(Missile, self).__init__(280 * self.scale,
192 * self.scale,
caption="Missiles")
self.set_mouse_visible(False)
self.game = Game(self)
self.schedule = pyglet.clock.schedule_interval(
func=self.update, interval=1.0 / self.frames)

def update(self, interval):
pass

def on_draw(self):
window.clear()
self.game.atualiza()

def on_resize(self, width, height):
# Inicializa a view
glViewport(0, 0, width, height)
glMatrixMode(gl.GL_PROJECTION)
glLoadIdentity()
# Inverte as coordenadas do eixo Y
glOrtho(0, width, height, 0, -1, 1)
# Aplica a escala
glScalef(self.scale, self.scale, 1.0)
glMatrixMode(gl.GL_MODELVIEW)

def on_key_press(self, symbol, modifiers):
# Verifica se uma tecla foi pressionada. Se foi, guarda em F
# 120 F$="" : IF PEEK(-16384)>127 THEN GET F$
self.game.pressionado = True

def crialabel(self, mensagem, x=None, y=None,
fonte='Times New Roman', tamanho=36):
"""Prepara uma mensagem de texto para ser exibida"""
x = x or self.width // 2
y = y or self.height // 2
return pyglet.text.Label(
mensagem, font_name=fonte, font_size=tamanho,
x=x, y=y, anchor_x='center', anchor_y='center')

Além disso, as cores do Apple II precisam ser definidas:
def rgb_to_f(r, g, b):
return(r / 255.0, g / 255.0, b / 255.0)

# Cores do modo de alta resolução HGR
# Fonte: https://github.com/AppleWin/AppleWin/issues/254
CORES = [(0.0, 0.0, 0.0), # Preto 0
rgb_to_f(20, 245, 60), # Verde 1
rgb_to_f(255, 68, 253), # Magenta 2
rgb_to_f(255, 255, 255), # Branco 3
rgb_to_f(255, 106, 60), # Laranja 5
rgb_to_f(20, 207, 253), # Azul médio 6
rgb_to_f(255, 255, 255), # Branco 7
]

e finalmente a classe Game com o jogo em si. Observar que os índices em Python começam em 0 e em Basic começam com 1. Primeiro passo da conversão:
class Game():
def __init__(self, parent):
self.cor = CORES
self.window = parent
# Cria dois vetores com 3 elementos cada
# 40 DIM Y(3),F(3)
self.Y = [0] * 3
self.F = [0] * 3
# Inicializa N igual a 1 e MS igual a 5
# 50 N=1 : MS=5
self.N = 0
self.MS = 5
# Gera um número aleatório entre 0 e 6 + 4
# 60 PS=INT(RND(1)*6+4)
self.PS = random.randint(0, 6) + 4
# Gera um número aleatório entre 0 e 135 + 11
# 70 P=INT(RND(1)*135+11)
self.P = random.randint(0, 135) + 11
self.estado = "jogando"
self.I = self.PS
self.pressionado = False
self.label = None

def atualiza(self):
if self.estado == "jogando":
self.jogue()
else:
if self.label:
glMatrixMode(gl.GL_PROJECTION)
glLoadIdentity()
glOrtho(0, self.window.width, 0, self.window.height, -1, 1)
glMatrixMode(gl.GL_MODELVIEW)
self.label.draw()
if self.pressionado:
pyglet.app.event_loop.exit()

def jogue(self):
# Desvia para subrotina
# 80 GOSUB 400
self.sub_400()
# Loop: repete I de PS até 265, incrementando de PS
# 90 FOR I=PS TO 265 STEP PS
if self.I > 265:
self.sub_250()
return
# Calcula os valores de X, Y e C. Desvia para subrotina em 300
# 100 X=I-PS : Y=159-P : C=0 : GOSUB 300
self.X = self.I - self.PS
self.y = 159 - self.P
self.C = 0
self.sub_300()
# Define X e C. Desvia para subrotina 300
# 110 X=I : C=3: GOSUB 300
self.X = self.I
self.C = 3
self.sub_300()
# Se algo foi pressionado ou se N>3 desvia para 160
# 130 IF F$="" OR N>3 THEN 160
if self.pressionado and self.N < 3:
# F[N] = 1
# 140 F(N)=1
self.F[self.N] = 1
# N+=1
# 150 N=N+1
self.N += 1
self.pressionado = False
# Repete J de 1 até 3
# 160 FOR J=1 TO 3
for self.J in range(0, 3):
# Zera C e desvia para subrotina da linha 350
# 170 C=0 : GOSUB 350
self.C = 0
self.sub_350()
# Se F[J]==0 ou Y[J]>145 desvia para 230
# 180 IF F(J)=0 OR Y(J)>145 THEN 230
if self.F[self.J] == 0 or self.Y[self.J] > 145:
continue
# Y[J]+=MS
# 190 Y(J)=Y(J)+MS
self.Y[self.J] += self.MS
# C=3. Desvia para subrotina da linha 350
# 200 C=3 : GOSUB 350
self.C = 3
self.sub_350()
# Calcula X e Y
# 210 X=J*70-I : Y=P-Y(J)
self.X = (self.J + 1) * 70 - self.I
self.y = self.P - self.Y[self.J]
# Se X>-1 e X<15 e Y>-9 e Y<5 desvia para 270
# 220 IF X>-1 AND X<15 AND Y>-9 AND Y<5 THEN 270
if self.X > -1 and self.X < 15 and self.y > -9 and self.y < 5:
self.sub_270()
# Fim do loop, volta para o for da linha 160
# 230 NEXT
# Fim do loop, volta para for da linha 90
# 240 NEXT
# 90 FOR I=PS TO 265 STEP PS
self.I += self.PS

def sub_250(self):
# Posiciona o cursor na linha 22 e imprime MISSED
# 250 VTAB 22 : PRINT "MISSED"
print("MISSED")
self.imprima("MISSED")
# Termina o programa
# 260 END
self.muda_estado("fimdejogo")

def sub_270(self):
# Posiciona o cursor na linha 22 e imprime HIT!!!
# 270 VTAB 22 : PRINT "HIT!!!"
print("HIT")
self.imprima("HIT!!!")
# Termina o programa
self.muda_estado("fimdejogo")

def sub_300(self):
# Troca a cor de desenho para C
# 300 HCOLOR=C
self.set_color(self.C)
# Traça uma linha de X,Y até X,Y-8
# 310 HPLOT X,Y TO X,Y-8
# Continua a linha
# 320 HPLOT TO X+3,Y-2 : HPLOT TO X+12, Y-2
# 330 HPLOT TO X+14,Y : HPLOT TO X,Y
pyglet.graphics.draw(6, pyglet.gl.GL_LINE_LOOP,
('v2i', (self.X, self.y,
self.X, self.y - 8,
self.X + 3, self.y - 2,
self.X + 12, self.y - 2,
self.X + 14, self.y,
self.X, self.y)))

# Retorno da subrotina
# 340 RETURN

def sub_350(self):
# Troca a cor para C
# 350 HCOLOR=C
self.set_color(self.C)
# Desenha linha
# 360 HPLOT 70*J, 158-Y(J) TO 70*J,154-Y(J)
J = self.J + 1
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2i', (70 * J, 158 - self.Y[self.J],
70 * J, 154 - self.Y[self.J])))
# Volta da subrotina
# 370 RETURN

def sub_400(self):
self.set_color(3)
# 400 FOR J=1 TO 3
for J in range(1, 4):
# Desenha linha
# 410 HPLOT 70*J-5,159 TO 70*J+5,159
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2i', (70 * J - 5, 159,
70 * J + 5, 159)))
# Fim do loop, volta para linha 400
# 420 NEXT
# Retorna da subrotina
# 430 RETURN

def set_color(self, color):
glColor3f(*self.cor[color])

def muda_estado(self, estado):
print("Mudança de Estado: {} --> {}".format(self.estado, estado))
self.estado = estado

def imprima(self, mensagem):
self.label = self.window.crialabel(mensagem)

Como em OpenGL limpamos a tela a cada frame, a rotina que apaga os objetos pode ser apagada. No caso, a chamada a sub_300() com C=0.
O nome dos métodos ainda não foram mudados, vamos renomear:
sub_250 para pedeu
sub_270 para acertou
sub_300 para desenha_aviao
sub_350 para desenha_tiro
sub_400 para desenha_bases
Apagando os comentários com o código em Basic, temos:
import pyglet
from pyglet.gl import *
import random


def rgb_to_f(r, g, b):
return(r / 255.0, g / 255.0, b / 255.0)

# Cores do modo de alta resolução HGR
# Fonte: https://github.com/AppleWin/AppleWin/issues/254
CORES = [(0.0, 0.0, 0.0), # Preto 0
rgb_to_f(20, 245, 60), # Verde 1
rgb_to_f(255, 68, 253), # Magenta 2
rgb_to_f(255, 255, 255), # Branco 3
rgb_to_f(255, 106, 60), # Laranja 5
rgb_to_f(20, 207, 253), # Azul médio 6
rgb_to_f(255, 255, 255), # Branco 7
]


class Game():
def __init__(self, parent):
self.cor = CORES
self.window = parent
# Cria dois vetores com 3 elementos cada
self.Y = [0] * 3
self.F = [0] * 3
# Inicializa N igual a 1 e MS igual a 5
self.N = 0
self.MS = 5
# Gera um número aleatório entre 0 e 6 + 4
self.PS = random.randint(0, 6) + 4
# Gera um número aleatório entre 0 e 135 + 11
self.P = random.randint(0, 135) + 11
self.estado = "jogando"
self.I = self.PS
self.pressionado = False
self.label = None

def atualiza(self):
if self.estado == "jogando":
self.jogue()
else:
if self.label:
glMatrixMode(gl.GL_PROJECTION)
glLoadIdentity()
glOrtho(0, self.window.width, 0, self.window.height, -1, 1)
glMatrixMode(gl.GL_MODELVIEW)
self.label.draw()
if self.pressionado:
pyglet.app.event_loop.exit()

def jogue(self):
self.desenha_bases()
if self.I > 265:
self.perdeu()
return
self.X = self.I - self.PS
self.y = 159 - self.P
self.C = 0
self.desenha_aviao()
self.X = self.I
self.C = 3
self.desenha_aviao()
if self.pressionado and self.N < 3:
self.F[self.N] = 1
self.N += 1
self.pressionado = False
for self.J in range(0, 3):
if self.F[self.J] == 0 or self.Y[self.J] > 145:
continue
self.Y[self.J] += self.MS
self.C = 3
self.desenha_tiro()
self.X = (self.J + 1) * 70 - self.I
self.y = self.P - self.Y[self.J]
if self.X > -1 and self.X < 15 and self.y > -9 and self.y < 5:
self.acertou()
self.I += self.PS

def perdeu(self):
self.imprima("MISSED")
self.muda_estado("fimdejogo")

def acertou(self):
self.imprima("HIT!!!")
self.muda_estado("fimdejogo")

def desenha_aviao(self):
self.set_color(self.C)
pyglet.graphics.draw(6, pyglet.gl.GL_LINE_LOOP,
('v2i', (self.X, self.y,
self.X, self.y - 8,
self.X + 3, self.y - 2,
self.X + 12, self.y - 2,
self.X + 14, self.y,
self.X, self.y)))

def desenha_tiro(self):
self.set_color(self.C)
J = self.J + 1
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2i', (70 * J, 158 - self.Y[self.J],
70 * J, 154 - self.Y[self.J])))

def desenha_bases(self):
self.set_color(3)
for J in range(1, 4):
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2i', (70 * J - 5, 159,
70 * J + 5, 159)))

def set_color(self, color):
glColor3f(*self.cor[color])

def muda_estado(self, estado):
print("Mudança de Estado: {} --> {}".format(self.estado, estado))
self.estado = estado

def imprima(self, mensagem):
self.label = self.window.crialabel(mensagem)


class Missile(pyglet.window.Window):
def __init__(self):
self.scale = 4 # Escala os gráficos 4x
self.frames = 8 # Frames por segundo.
# Define quantas vezes vamos atualizar a tela por segundo
super(Missile, self).__init__(280 * self.scale,
192 * self.scale,
caption="Missiles")
self.set_mouse_visible(False)
self.game = Game(self)
self.schedule = pyglet.clock.schedule_interval(
func=self.update, interval=1.0 / self.frames)

def update(self, interval):
pass

def on_draw(self):
window.clear()
self.game.atualiza()

def on_resize(self, width, height):
# Inicializa a view
glViewport(0, 0, width, height)
glMatrixMode(gl.GL_PROJECTION)
glLoadIdentity()
# Inverte as coordenadas do eixo Y
glOrtho(0, width, height, 0, -1, 1)
# Aplica a escala
glScalef(self.scale, self.scale, 1.0)
glMatrixMode(gl.GL_MODELVIEW)

def on_key_press(self, symbol, modifiers):
self.game.pressionado = True

def crialabel(self, mensagem, x=None, y=None,
fonte='Times New Roman', tamanho=36):
"""Prepara uma mensagem de texto para ser exibida"""
x = x or self.width // 2
y = y or self.height // 2
return pyglet.text.Label(
mensagem, font_name=fonte, font_size=tamanho,
x=x, y=y, anchor_x='center', anchor_y='center')


window = Missile()
pyglet.app.run()

Bem melhor, mas ainda guarda várias características do programa em Basic. O nome das variáveis é uma catástrofe. Renomeando as variáveis e melhorando os comentários, o programa completo fica assim:
import pyglet
from pyglet.gl import *
import random


def rgb_to_f(r, g, b):
return(r / 255.0, g / 255.0, b / 255.0)

# Cores do modo de alta resolução HGR
# Fonte: https://github.com/AppleWin/AppleWin/issues/254
CORES = [(0.0, 0.0, 0.0), # Preto 0
rgb_to_f(20, 245, 60), # Verde 1
rgb_to_f(255, 68, 253), # Magenta 2
rgb_to_f(255, 255, 255), # Branco 3
rgb_to_f(255, 106, 60), # Laranja 5
rgb_to_f(20, 207, 253), # Azul médio 6
rgb_to_f(255, 255, 255), # Branco 7
]


class Game():
def __init__(self, parent):
self.cor = CORES
self.window = parent
# Cria dois vetores com 3 elementos cada
self.Y = [0] * 3 # Altura do tiro
self.F = [0] * 3 # Estado do tiro
self.tiros_disparados = 0 # tiros já disparados
self.velocidade_do_tiro = 5
# Gera um número aleatório entre 0 e 6 + 4
self.velocidade_aviao = random.randint(0, 6) + 4
# Gera um número aleatório entre 0 e 135 + 11
self.altura_do_aviao = random.randint(0, 135) + 11
self.estado = "jogando"
self.posicao_do_aviao = self.velocidade_aviao
self.pressionado = False
self.label = None

def atualiza(self):
if self.estado == "jogando":
self.jogue()
else:
if self.label:
glMatrixMode(gl.GL_PROJECTION)
glLoadIdentity()
glOrtho(0, self.window.width, 0, self.window.height, -1, 1)
glMatrixMode(gl.GL_MODELVIEW)
self.label.draw()
if self.pressionado:
pyglet.app.event_loop.exit()

def jogue(self):
self.desenha_bases()
if self.posicao_do_aviao > 265:
self.perdeu()
return
self.y = 159 - self.altura_do_aviao
self.X = self.posicao_do_aviao
self.C = 3
self.desenha_aviao()
if self.pressionado and self.tiros_disparados < 3:
self.F[self.tiros_disparados] = 1
self.tiros_disparados += 1
self.pressionado = False
self.processa_tiros()
self.posicao_do_aviao += self.velocidade_aviao

def processa_tiros(self):
for self.J in range(0, 3):
if self.F[self.J] == 0 or self.Y[self.J] > 145:
continue
self.Y[self.J] += self.velocidade_do_tiro
self.C = 3
self.desenha_tiro()
self.X = (self.J + 1) * 70 - self.posicao_do_aviao
self.y = self.altura_do_aviao - self.Y[self.J]
if self.X > -1 and self.X < 15 and self.y > -9 and self.y < 5:
self.acertou()

def perdeu(self):
self.imprima("MISSED")
self.muda_estado("fimdejogo")

def acertou(self):
self.imprima("HIT!!!")
self.muda_estado("fimdejogo")

def desenha_aviao(self):
self.set_color(self.C)
pyglet.graphics.draw(6, pyglet.gl.GL_LINE_LOOP,
('v2i', (self.X, self.y,
self.X, self.y - 8,
self.X + 3, self.y - 2,
self.X + 12, self.y - 2,
self.X + 14, self.y,
self.X, self.y)))

def desenha_tiro(self):
self.set_color(self.C)
J = self.J + 1
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2i', (70 * J, 158 - self.Y[self.J],
70 * J, 154 - self.Y[self.J])))

def desenha_bases(self):
self.set_color(3)
for J in range(1, 4):
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2i', (70 * J - 5, 159,
70 * J + 5, 159)))

def set_color(self, color):
glColor3f(*self.cor[color])

def muda_estado(self, estado):
print("Mudança de Estado: {} --> {}".format(self.estado, estado))
self.estado = estado

def imprima(self, mensagem):
self.label = self.window.crialabel(mensagem)


class Missile(pyglet.window.Window):
def __init__(self):
self.scale = 4 # Escala os gráficos 4x
self.frames = 8 # Frames por segundo.
# Define quantas vezes vamos atualizar a tela por segundo
super(Missile, self).__init__(280 * self.scale,
192 * self.scale,
caption="Missiles")
self.set_mouse_visible(False)
self.game = Game(self)
self.schedule = pyglet.clock.schedule_interval(
func=self.update, interval=1.0 / self.frames)

def update(self, interval):
pass

def on_draw(self):
window.clear()
self.game.atualiza()

def on_resize(self, width, height):
# Inicializa a view
glViewport(0, 0, width, height)
glMatrixMode(gl.GL_PROJECTION)
glLoadIdentity()
# Inverte as coordenadas do eixo Y
glOrtho(0, width, height, 0, -1, 1)
# Aplica a escala
glScalef(self.scale, self.scale, 1.0)
glMatrixMode(gl.GL_MODELVIEW)

def on_key_press(self, symbol, modifiers):
self.game.pressionado = True

def crialabel(self, mensagem, x=None, y=None,
fonte='Times New Roman', tamanho=36):
"""Prepara uma mensagem de texto para ser exibida"""
x = x or self.width // 2
y = y or self.height // 2
return pyglet.text.Label(
mensagem, font_name=fonte, font_size=tamanho,
x=x, y=y, anchor_x='center', anchor_y='center')


window = Missile()
pyglet.app.run()

Em um próximo artigo, vou continuar a refatorar o código. Tiro e Avião são claramente classes. Animação deixa a desejar. Mas é o divertido da programação, você sempre pode melhorar.

por Nilo Menezes (noreply@blogger.com) em 14 de May de 2016 às 17:35

May 13, 2016

PythonClub

Python com Unittest, Travis CI, Coveralls e Landscape (Parte 3 de 4)

Fala pessoal, tudo bem?

Na segunda parte deste tutorial, aprendemos a usar o Travis CI para automatizar os testes do nosso projeto, facilitando a manutenção do código quando temos vários colaboradores. Nesta terceira parte, vamos configurar o serviço Coveralls para que o mesmo gere relatórios de teste sobre o nosso projeto. Os relatórios são muito úteis quando desejamos verificar o quanto do nosso projeto está coberto por testes, evitando assim que alguma feature importante fique de fora. Assim como o Travis CI, o Coveralls será executado após cada push ou pull request.

Diferente do tutorial anterior, serei breve sobre o processo de inscrição do Coveralls, focando mais no seu uso.

Criando uma conta

Antes de começarmos a usar o Coveralls precisamos criar uma conta no serviço. Isso pode ser feito aqui. O serviço é totalmente gratuíto para projetos opensource.

Após a inscrição, você será levado para uma nova página com uma listagem dos repositórios que você possui no Github.

Na imagem acima já podemos visualizar o projeto que estou usando neste tutorial: codigo-avulso-test-tutorial. Caso o seu repositório não esteja na lista, clique no botão ADD REPOS no canto superior direito da tela.

Ao clicar no botão, você será redirecionado a uma página onde é possível slecionar quais repositórios serão analisados pelo Coveralls. Caso o repositório desejado não esteja na lista, clique no botão RE-SYNC REPOS no canto superior direito. Ele vai realizar o escaneamento do seu perfil no Github e importar seus projetos.

Clique no botão escrito OFF ao lado esquerdo do nome do repositório. Isso ativará o serviço para este repositório.

Clique no botão DETAILS ao lado direito do nome do repositório e você será redirecionado para uma tela de configuração. Aqui o passo mais interessante é pegar a url da badgepara usarmos em nosso README.md.

Coverage Status

Na área superior da tela, temos o seguinte:

Clique em EMBED e uma janelá de dialogo irá se abrir, selecione e copie o código em MARKDOWN.

Agora cole o código no cabeçalho do seu arquivo README.md, semelhante ao que fizemos com o Travis CI no tutorial anterior.

# Codigo Avulso Test Tutorial
[![Build Status](https://travis-ci.org/mstuttgart/codigo-avulso-test-tutorial.svg?branch=master)](https://travis-ci.org/mstuttgart/codigo-avulso-test-tutorial)

[![Coverage Status](https://coveralls.io/repos/github/mstuttgart/codigo-avulso-test-tutorial/badge.svg?branch=master)](https://coveralls.io/github/mstuttgart/codigo-avulso-test-tutorial?branch=master)

Concluída esta estapa, o próximo passo será adicionarmos o serviço em nosso projeto no Github.

Adicionando o Coveralls

Vamos adicionar o serviço durante o processo de teste do projeto. Assim, depois de cada push ou pull request, o Coveralls irá gerar o relatório sobre nossos testes.

Abra o arquivo .travis.yml em seu editor. Teremos o seguinte código:

language: python

python:
  - "2.7"

sudo: required

script:
  - run setup.py test

Agora vamos alterá-lo adicionando a funcionalidade do Coveralls. O códio atualizado do .travis.yml pode ser visto a seguir:

language: python

python:
  - "2.7"

sudo: required

install:
  - pip install coveralls

script:
  - coverage run --source=codigo_avulso_test_tutorial setup.py test

after_success:
  - coveralls

Tag "install":

Aqui adicionamos o comando

pip install coveralls

A instalação do coveralls é necessaria para que possamos gerar os relatórios. Você pode instalá-lo em sua máquina e gerar relátorios em html. Fica a sugestão de estudo.

Tag "script":

Aqui substimuímos o comando

run setup.py test

por

coverage run --source=codigo_avulso_test_tutorial setup.py test

Esse comando executa os mesmo testes de antes, mas já prove um relatório sobre a cobertura de testes do seu código.

Tag "after_success":

A última alteração foi adicionar a tag after_success.

after_success:
  - coveralls

Essa tag indica que após a execuação bem sucedida dos testes, deve-se iniciar o serviço de analise do Coveralls.

Assim que terminar de fazer essas alterações você já pode enviar o seu código para o Github. Assim que subir o código, o Travis CI irá iniciar o processo de teste. Finalizando os testes, o Coveralls será iniciado. Se tudo ocorrer bem, a badge que adicionamos no aquivo README.md do projeto será atualizada exibindo a porcentagem do nosso código que está coberta por testes. Você pode clicar na badge ou ir até o seu perfil no site do Coveralls e verificar com mais detalhes as informações sobre seu projeto.

Na seção LATEST BUILDS clique no último build disponível que será possível verificar a porcentagem cobertura de teste para cada arquivo do seu projeto.

Caso tenha interessa, aqui está o link do repositorio que usei para esse tutorial: codigo-avulso-test-tutorial.

Conclusão

Aqui encerramos a terceira parte do nossa série de tutoriais sobre Unittest. O Coveralls ainda possui muitas configurações não mostradas aqui, então se você se interessar, fica a sugestão de estudo. No próximo tutorial veremos como utilizar o Landscape, um linter que analise nossos códigos atrás de problemas de sintaxe, formatação e possíveis erros de códigos (variáveis não declaradas, varíaveis com escopo incorreto e etc).

É isso pessoal. Obrigado por ler até aqui e até o próximo tutorial!

Publicado originalmente: python-com-unittest-travis-ci-coveralls-e-landscape-parte-3-de-4

por Michell Stuttgart em 13 de May de 2016 às 15:25

May 12, 2016

Programando Ciência

Complex scatter plots on Python [PART II] – Defining colors, labels and title

Hey scientist! How is it going?
In the second post of this series we’ll improve our preliminary scatter plot, obtained from IBGE [1] data, defining a color for each region, adding labels and title. Then, our plot becomes more informative. Let’s do this!

Just to remember, on our first post [2] we used the following codes to create our preliminary plot:

# importing necessary packages.
import matplotlib.pyplot as plt
import pandas as pd

# reading data_ibge.xls
data_brazil = pd.read_excel('data_ibge.xls', sheetname=2)

# generating the plot.
plt.scatter(x = data_brazil['LifeExpec'],
            y = data_brazil['GDPperCapita'],
            s = data_brazil['PopX1000'])
plt.show()

With these commands we imported Pandas [3] and matplotlib [4], read the file data_ibge.xls [5], and generated the following plot:

scatter_preliminary

The axes of this plot present the life expectancy and GDP per capita, and the population of each state is represented by the size of each circle. But we still don’t know which state is each one, or the region where it is.

To put more information on our plot, let’s add labels and title to it. Also, we’ll define a color for each region, so each state will have a color related to its region.

First we’ll create a list with the hexa values from the color palette 5-class Dark2, from ColorBrewer2 [6]. This vector has five colors, each one representing a Brazilian region:

colors = ['#1b9e77',
          '#d95f02',
          '#7570b3',
          '#e7298a',
          '#66a61e']

Now we define a function, attribute_color(), which has a dictionary pointing to the color related to each region. When the function receives a region which isn’t defined on the dictionary, attribute_color() returns the color black.

def attribute_color(region):
    colors = {
        'North':'#1b9e77',
        'Northeast':'#d95f02',
        'Southeast':'#7570b3',
        'South':'#e7298a',
        'Central-West':'#66a61e'
    }
    return colors.get(region, 'black')

Then we create the color vector, a list which receives the color of each state according to its region. The quantity of states is calculated by the length len(data_brazil[‘Region’]), and the list receives each value using append().

color_region = list()
qty_states = len(data_brazil['Region'])

for state in range(qty_states):
    color_region.append(attribute_color(data_brazil['Region'][state]))

Using print() we can see the color vector:

print(color_region)

['#1b9e77', '#1b9e77', '#1b9e77', '#1b9e77', '#1b9e77', '#1b9e77', '#1b9e77', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#7570b3', '#7570b3', '#7570b3', '#7570b3', '#e7298a', '#e7298a', '#e7298a', '#66a61e', '#66a61e', '#66a61e', '#66a61e']

Now we’ll use color_region as an argument in matplotlib’s scatter(). Also, we’ll pass the argument alpha = 0.6, which will show the circles more transparent:

plt.scatter(x = data_brazil['LifeExpec'],
            y = data_brazil['GDPperCapita'],
            s = data_brazil['PopX1000'],
            c = color_region,
            alpha = 0.6)

Then we put the title, labels on X and Y axis, and also a grid:

plt.title('Brazilian development in 2013, according to each state', fontsize=22)
plt.xlabel('Life expectancy (years)', fontsize=22)
plt.ylabel('GDP per capita (R$)', fontsize=22)
plt.grid(True)

The argument fontsize=22 increases the size of the font shown.

We use plt.show() to present the plot. Check the resulting picture:

scatter_preliminaryII.jpg

Our plot is a lot better than the initial! Now, for instance, we can see that the states tend to be close, according to their region.

We don’t know which state is each one, or estimate the amount of people… we also need to know which color represents each region. We’ll solve this on the next week, with more labels and legends! Stay with us!

Thanks scientist! Gigaregards, see you next time!


Did you like this post? Please comment and share with your friends!
Want to download Programando Ciência codes? Go to our GitHub!
Make a donation for Programando Ciência!
Like us also on Facebook: www.facebook.com/programandociencia
I’m on Twitter! Follow me if you can! @alexdesiqueira

 


por alexandrejaguar em 12 de May de 2016 às 16:49

Gráficos de dispersão complexos no Python [PARTE II] – Definindo cores, rótulos e título

Faaaaaaaaaaaaala cientista! Tudo certo?
No segundo post da série vamos melhorar nosso gráfico de dispersão preliminar, obtido a partir dos dados do IBGE [1]. Definiremos uma cor para cada região, adicionaremos rótulos e título. Com isso o gráfico começa a ficar mais informativo. Vamos lá?

Só pra lembrar, no primeiro post da série [2] utilizamos os códigos a seguir para definir nosso gráfico preliminar:

# importando os pacotes necessários.
import matplotlib.pyplot as plt
import pandas as pd

# lendo o arquivo dados_ibge.xls
dados_brasil = pd.read_excel('dados_ibge.xls', sheetname=2)

# gerando o gráfico.
plt.scatter(x = dados_brasil['ExpecVida'],
            y = dados_brasil['PIBperCapita'],
            s = dados_brasil['PopX1000'])
plt.show()

Com eles importamos o Pandas [3] e o matplotlib [4], lemos o arquivo dados_ibge.xls [5] e geramos o gráfico a seguir:

scatter_preliminar

Os eixos desse gráfico apresentam a expectativa de vida e o PIB per capita, e a população de cada estado é representada pelo tamanho de cada círculo. Mas ainda não sabemos qual é cada estado ou região.

Para que nosso gráfico fique mais informativo, adicionaremos rótulos e título. Além disso, para que cada estado possua uma cor relacionada à região na qual ele está, vamos definir uma cor para cada região.

Primeiro criaremos uma lista com os valores hexa da paleta de cores 5-class Dark2, do ColorBrewer2 [6]. Esse vetor tem cinco cores, cada uma representando uma região do Brasil:

cores = ['#1b9e77',
         '#d95f02',
         '#7570b3',
         '#e7298a',
         '#66a61e']

Agora definiremos uma função, atribui_cor(), que possui um dicionário apontando a cor correspondente a cada região. Quando a função recebe uma região que não está definida no dicionário, atribui_cor() retorna a cor preta, ‘black’.

def atribui_cor(regiao):
    cores = {
        'Norte':'#1b9e77',
        'Nordeste':'#d95f02',
        'Sudeste':'#7570b3',
        'Sul':'#e7298a',
        'CentroOeste':'#66a61e'
    }
    return cores.get(regiao, 'black')

Então criaremos o vetor de cores, uma lista que receberá a cor de cada estado de acordo com a região. A quantidade de estados é calculada pelo comprimento len(dados_brasil[‘Regiao’]), e a lista recebe cada valor usando append().

cor_regiao = list()
qtde_estados = len(dados_brasil['Regiao'])

for estado in range(qtde_estados):
    cor_regiao.append(atribui_cor(dados_brasil['Regiao'][estado]))

Usando print() vemos o vetor com as cores:

print(cor_regiao)

['#1b9e77', '#1b9e77', '#1b9e77', '#1b9e77', '#1b9e77', '#1b9e77', '#1b9e77', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#d95f02', '#7570b3', '#7570b3', '#7570b3', '#7570b3', '#e7298a', '#e7298a', '#e7298a', '#66a61e', '#66a61e', '#66a61e', '#66a61e']

Usaremos o vetor cor_regiao como argumento em scatter(), do matplotlib. Além disso, passaremos também o argumento alpha = 0.6, que deixará os círculos mais transparentes:

plt.scatter(x = dados_brasil['ExpecVida'],
            y = dados_brasil['PIBperCapita'],
            s = dados_brasil['PopX1000'],
            c = cor_regiao,
            alpha = 0.6)

Então, colocaremos o título, rótulos nos eixos X e Y, e grade:

plt.title('Desenvolvimento do Brasil em 2013, por estado', fontsize=22)
plt.xlabel('Expectativa de vida (anos)', fontsize=22)
plt.ylabel('PIB per capita (R$)', fontsize=22)
plt.grid(True)

O argumento fontsize=22 aumenta o tamanho da fonte exibida.

Para mostrar o gráfico, usamos plt.show(). A figura resultante é dada a seguir:

scatter_preliminarII.jpg

Nosso gráfico já melhorou em relação ao inicial! Agora, por exemplo, conseguimos ver que os estados tendem a estar próximos, de acordo com a sua região.

Ainda não conseguimos saber qual é cada estado, ou estimar qual é a quantidade de pessoas… também precisamos saber que cor representa cada região. Resolveremos isso na semana que vem, com mais rótulos e legendas! Fique conosco!

Valeu cientista! Um giga abraço e até a próxima!


Gostou? Curta e compartilhe com seus amigos!
Quer baixar os códigos do Programando Ciência? Corre lá no nosso GitHub!
Faça uma doação pro Programando Ciência!
Curta a gente também no Facebook: www.facebook.com/programandociencia
Estou no Twitter! Siga-me se puder! @alexdesiqueira

 


por alexandrejaguar em 12 de May de 2016 às 16:44

May 10, 2016

Magnun Leno

VIM: Mais que um Editor

Há muito tempo, numa galáxia muito muito distante… Eu fui convidado para palestrar no Calango Hacker Club sobre VIM.

VIM - Star Wars

Como eu não queria correr o risco de dar uma palestra falando sobre algum aspecto específico do VIM e não conseguir capturar a atenção dos presentes, acabei criando uma palestra de introdução ao VIM, na qual eu um pouco de história, as características básicas deste editor e muitas funcionalidades básicas para quem ainda não conhece direito ainda essa ferramenta.

VIM: Mais que um Editor é um artigo original de Mind Bending

por Magnun em 10 de May de 2016 às 16:10

May 08, 2016

PythonClub

Python com Unittest, Travis CI, Coveralls e Landscape (Parte 2 de 4)

Fala pessoal, tudo bem?

Na primeira parte deste tutorial, aprendemos como criar testes para nosso projeto. Nesta segunda parte, vamos configurar o serviço Travis CI para que o mesmo execute os testes do nosso projeto diretamente no github. Isso é especialmente útil quando possuímos várias pessoas trabalhando em um mesmo projeto, pois o Travis CI executa os testes após cada push ou pull request. Dessa forma garantimos que um determinado commit não irá "quebrar" nossa aplicação.

Antes de inicarmos nosso trabalho de configuração do Travis CI, vamos aprender um pouco mais sobre esse serviço.

Sobre o Travis CI

Travis CI é uma ferramenta online que permite executar o deploy de sua aplicação, rodando de maneira automática os testes do seu projeto hospedado no Github. Através dele é possível manter um histórico dos testes para cada commit do seu projeto, executar testes em paralelo, além do suporte a diversas linguagens de programação. Você pode, por exemplo, verificar se seu projeto funciona corretamente tanto com Python 2.7, quanto com o Python 3.

Após a execução do teste, recebemos um email nos informando se o teste foi bem sucedido ou se houve alguma falha. O serviço é totalmente gratuito para projetos opensource.

Alt Text

Criando uma conta

Para utilizarmos o Travis CI em nosso projeto, precisamos primeiro realizar nosso cadastro no serviço. Para isso acesse https://travis-ci.org/.

Logo no topo direito da página, temos o botão abaixo, para nos inscrevermos usando nossa conta no Github.

Ao pressionar o botão, você será direcionado para a página a seguir:

Realize o login com seu usuário/senha do Github. Assim que realizar o login, na canto superior direito da tela, clique no seu nome de usuário e, em seguida, em "Accounts". Com isso, uma tela com todos os repositórios que você tem permissão de escrita (repositórios pessoais, de organização, forks e etc) será exibida.

Agora vamos ativar o serviço para o repositório que criei na primeira parte do tutorial: codigo-avulso-test-tutorial. Basta clicar no botão "X" ao lado esquerdo do nome do seu repositório. Ele ficará assim:

Bom, a partir de agora, seu repositório está pronto para o usar o Travis CI, porém antes precisamos configurar os parâmetros de teste do nosso projeto.

Configurando o Travis CI em nosso repositório

No diretório raiz do nosso projeto, vamos criar um arquivo chamado .travis.yml.

touch .travis.yml

Observe que o nome do arquivo obrigatoriamente deve inciar com ponto. Após criarmos o arquivo, teremos a seguinte estrutura de diretórios:

.
├── codigo_avulso_test_tutorial
│   ├── circulo.py
│   ├── circulo.pyc
│   ├── figura_geometrica.py
│   ├── figura_geometrica.pyc
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── quadrado.py
│   └── quadrado.pyc
├── codigo_avulso_test_tutorial.egg-info
│   ├── dependency_links.txt
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   └── top_level.txt
├── LICENSE
├── README.md
├── setup.py
├── test
│   ├── circulo_test.py
│   ├── circulo_test.pyc
│   ├── figura_geometrica_test.py
│   ├── figura_geometrica_test.pyc
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── quadrado_test.py
│   └── quadrado_test.pyc
└── .travis.yml

Esse é nosso arquivo de configuração. Nele vamos adicionar qual linguagen nosso projeto utiliza, de quais módulos e pacotes ele depende, entre outras inúmeros ajustes, dependendo do seu projeto. Aqui, vou mostrar as configurações básicas que utilizo, para que o tutorial não fique muito extenso. Então, abra o arquivo .travis.yml em seu editor preferido e adicione o seguinte código.

language: python

python:
  - "2.7"

sudo: required

script:
  - python setup.py test

Agora vamos explicar cada tag do arquivo:

  • language: podemos definir qual linguagem nosso projeto utiliza. Se este parâmetro não for incluso, o Travis CI irá considerar a linguagem ruby como default.
  • python: aqui definimos que os testes serão executados usando o Python 2.7 e se desejarmos, também podemos adicionar outras versões do Python.
  • sudo: usado para executar o Travis CI como permissão de usuário root. Necessário caso você deseje instalar alguma dependência usando o comando apt-get install nomepacote.
  • script: nessa tag, finalmente vamos executar nosso script de teste.

Dica: neste link você pode colar o código do seu arquivo .travis.yml para verificar se o mesmo está correto.

Adicionado uma badge para o repositório

O próximo passo é é adicionar uma badge para o nosso repositório. Isso não é obrigatório, mas ajuda você, sua equipe e outras pessoas que se interessarem pelo seu repositório, a visualizar o status da execução dos testes e verificar se seu código está funcionando corretamente.

Build Status

Na tela onde você ativou o Travis CI para seu reposiorio, clique no símbolo da engrenagem.

Na nova tela, podemos realizar algumas configurações, como por exemplo se o Travis CI será executado para push e para pull requests e também podemos pegar a badge. Ao clicarmos no botão logo ao lado do nome do repositório, uma janela será exibida.

Selecione a branch a ser observada pelo Travis CI, escolha a opção Markdown e copie o código que aparecerá na caixa de texto para o arquivo README.md do seu repositório. O meu README.md ficou assim:

# Codigo Avulso Test Tutorial
[![Build Status](https://travis-ci.org/mstuttgart/codigo-avulso-test-tutorial.svg?branch=master)](https://travis-ci.org/mstuttgart/codigo-avulso-test-tutorial)

Com esses passos, quando algum push ou pull request for enviado ao repositório, o Travis CI executará os testes, garantindo assim o funcionamento estável do nosso código e nos avisando caso alguma modificação venha causar algum erro em nossa aplicação.

Vale lembrar que o tempo para deploy pode variar, dependendo da quantidade de testes do seu projeto, quantidade de dependências a serem instaladas e etc.

Conclusão

Aqui encerramos a segunda parte do nossa série de tutoriais sobre Unittest. Eu decidi separar a série em 4 partes para que cada uma fosse explicada com mais detalhes mas sem deixar o tutorial muito extenso. O Travis IC ainda possui muitas configurações não abordadas aqui, então se você se interessar, pode dar uma olhada na sua documentação oficial aqui. No próximo tutorial veremos como utilizar o Coveralls para gerar relatórios dos nossos testes.

É isso pessoal. Obrigado por ler até aqui!

Até o próximo tutorial!

Publicado originalmente: python-com-unittest-travis-ci-coveralls-e-landscape-parte-2-de-4

por Michell Stuttgart em 08 de May de 2016 às 23:34

Programando Ciência

Complex scatter plots on Python [PART I] – Obtaining data and creating a preliminary plot

Hey scientist! How is it going?
In this post series we’ll discuss the scatter (or dispersion) plots, which can be used to present several data more intuitively. Today we’ll start obtaining the desired data, seeking some info on IBGE [1]. Let’s do this!

First we obtain the data for our plot: in this example, we want the info about population, GDP per capita and life expectancy of Brazilian states in 2013. These data were obtained from two online articles from Sala de Imprensa, IBGE ([2], [3]), and a part of these tables is given below:

tabela_pibpoptabela_expecvida

After we get these tables, we need to organize the data. This way we can read it easier on Python. In this example, we copy the two tables in a LibreOffice [4] spreadsheet and record all into one xls file (“População e PIB”, meaning “Population and GDB”, and “Expectativa de vida”, meaning “Life expectancy”, in [5]).

On the first table, “População e PIB”, we are interested on the columns “População residente (1.000 hab.)(1)” and “Produto Interno Bruto per capita R$”. On the second table, “Expectativa de vida”, we want the column “Esperança de vida ao nascer – 2013 – Total”. Then, we create a third table, named “Table for analysis”, and we paste the three interest columns: “GDPperCapita”, “LifeExpec” and “PopX1000”, according to the data contained on the original ones.

There you go! We already have data for a good plot. But we’ll show also the state and the region where it belongs. For that we create the columns “UF” and “Region” on “Table for analysis”. They contain the abbreviation of the state and the region where it is, respectively. Download the file with both original tables and the analysis data in [5] (Click on ‘View Raw’). Check the table containing the data analysis below:

table_analysis

 

After downloading the file in [5], you can open Spyder [6] and use the function read_excel() from Pandas [7] to read the table:

import pandas as pd

data_brazil = pd.read_excel('data_ibge.xls', sheetname=2)
data_brazil.head()

The argument sheetname=2 indicates we wish Pandas to read the third table.

Do you remember Python starts to count from zero? Then, we would have sheetname=0 for the first one and sheetname=1 for the second one. Actually, if you want the first table, you don’t need to use this argument.

After reading the table and putting it in the variable data_brazil, we want to see its contents. Pandas shows the first lines of the table when we use the command data_brazil.head(). The result follows:

data_head

Check that the columns represent the data from the LibreOffice spreadsheet. To manipulate the column contents independently, use the name of the column between brackets:

data_brazil['UF']
data_brazil['Region']
data_brazil['GDPperCapita']
data_brazil['LifeExpec']
data_brazil['PopX1000']

Try it!

Now we’ll create a preliminary dispersion plot, based on the collected data. For that we’re using scatter(), a function on matplotlib [8]:

import matplotlib.pyplot as plt

plt.scatter(x = data_brazil['LifeExpec'],
            y = data_brazil['GDPperCapita'],
            s = data_brazil['PopX1000'])
plt.show()

In this example, we use the life expectancy as X (argument x = data_brazil[‘LifeExpec’]), and the GDP per capita as Y (y = data_brazil[‘GDPperCapita’]). The population indicates the size of each circle (s = data_brazil[‘PopX1000’]). Check the resultant plot below:

scatter_preliminary

Well… this plot isn’t very informative. What is the little circle above, on the right? Which state has the highest life expectancy? Are the regions well distributed?

We’ll start to improve this plot on the next week, setting labels, title, text, legend, and colors. Stay with us!

Thanks scientist! Gigaregards, see you next time!

Updates!

May, 12: a new version of dados_ibge.xls, data_ibge.xls, is available. “Table for analysis” and the variables are now in English. Also corrected some wrong data, as pointed by A C Censi on the comments.


Did you like this post? Please comment and share with your friends!
Want to download Programando Ciência codes? Go to our GitHub!
Make a donation for Programando Ciência!
Like us also on Facebook: www.facebook.com/programandociencia
I’m on Twitter! Follow me if you can! @alexdesiqueira


por alexandrejaguar em 08 de May de 2016 às 17:21

Gráficos de dispersão complexos no Python [PARTE I] – Obtendo dados e criando um gráfico preliminar

Faaaaaaaaaaaaala cientista! Tudo certo?
Nessa série de posts vamos discutir os gráficos de dispersão, que podem ser utilizados para exibir vários dados de uma forma mais intuitiva. Hoje começaremos com a obtenção das informações desejadas, buscando algumas informações no IBGE [1]. Vamos lá?

Primeiro obteremos os dados para o nosso gráfico: neste exemplo, queremos as informações sobre a população, PIB per capita e expectativa de vida do Brasil em 2013, por estado. Esses dados foram obtidos em duas matérias da Sala de Imprensa do IBGE ([2],  [3]), e uma parte das tabelas é dada a seguir:

tabela_pibpoptabela_expecvida

Após obtermos essas tabelas, precisamos organizar os dados. Dessa forma, conseguiremos passá-los mais facilmente ao Python. Neste exemplo, copiamos as duas tabelas em uma planilha do LibreOffice [4] e gravamos tudo em um arquivo de extensão xls (“População e PIB” e “Expectativa de vida” em [5]).

Na primeira tabela, “População e PIB”, estamos interessados nas colunas “População residente (1.000 hab.)(1)” e “Produto Interno Bruto per capita R$”. Na segunda tabela, “Expectativa de vida”, queremos a coluna “Esperança de vida ao nascer – 2013 – Total”. Então, criamos uma terceira tabela, chamada “Tabela para análise”, e colamos as três colunas de interesse: elas recebem os nomes “PIBperCapita”, “ExpecVida” e “PopX1000”, de acordo com os dados contidos nas originais.

Pronto! Já temos dados para um bom gráfico. Mas além disso, mostraremos também o estado e a região na qual ele está. Para isso criamos em “Tabela para análise” as colunas “UF” e “Regiao”, que armazenam a sigla da unidade federativa e a que região ela pertence, respectivamente. Baixe o arquivo com as duas tabelas originais e a tabela para análise em [5] (Clique em ‘View Raw’). Veja a seguir a tabela para análise final:

tabela_analise

Após baixar o arquivo em [5], você pode abrir o Spyder [6] e utilizar a função read_excel() do Pandas [7] para ler a tabela:

import pandas as pd

dados_brasil = pd.read_excel('dados_ibge.xls', sheetname=2)
dados_brasil.head()

O argumento sheetname=2 indica que queremos que o Pandas leia a terceira tabela.

Lembra que o Python começa a contar no zero? Então, teríamos sheetname=0 para a primeira e sheetname=1 para a segunda. Mais ainda: se você quiser a primeira tabela, não precisa usar esse argumento.

Depois de ler a tabela e colocá-la na variável dados_brasil, queremos ver seu conteúdo. Para que o Pandas mostre as primeiras linhas da tabela, utilize o comando dados_brasil.head(). O resultado é o seguinte:

dados_head

Veja que as colunas representam os dados da panilha do LibreOffice. Para manipular o conteúdo das colunas de forma independente, utilize o nome da coluna entre colchetes:

dados_brasil['UF']
dados_brasil['Regiao']
dados_brasil['PIBperCapita']
dados_brasil['ExpecVida']
dados_brasil['PopX1000']

Experimente!

Criaremos agora um gráfico de dispersão preliminar, baseado nos dados que coletamos. Para isso utilizaremos a função scatter(), da biblioteca matplotlib [8]:

import matplotlib.pyplot as plt

plt.scatter(x = dados_brasil['ExpecVida'],
            y = dados_brasil['PIBperCapita'],
            s = dados_brasil['PopX1000'])
plt.show()

Nesse exemplo, utilizamos a expectativa de vida como o eixo X (argumento x = dados_brasil[‘ExpecVida’]), e o PIB per capita como o eixo Y (y = dados_brasil[‘PIBperCapita’]). A população indica o tamanho de cada círculo (s = dados_brasil[‘PopX1000’]). Veja a seguir o gráfico resultante:

scatter_preliminar.jpg

Bom… esse gráfico não é muito informativo. Que bolinha é aquela acima, à direita? Qual o estado com maior expectativa de vida? Será que as regiões estão bem distribuídas?

Começaremos a melhorar nosso gráfico na próxima semana! Colocaremos rótulos, título, texto, legenda e cores. Continue conosco!

Valeu cientista! Um giga abraço e até a próxima!

Atualizações!

Maio, 12: corrigidos alguns dados da tabela, como apontado por A C Censi nos comentários.


Gostou? Curta e compartilhe com seus amigos!
Quer baixar os códigos do Programando Ciência? Corre lá no nosso GitHub!
Faça uma doação pro Programando Ciência!
Curta a gente também no Facebook: www.facebook.com/programandociencia
Estou no Twitter! Siga-me se puder! @alexdesiqueira


por alexandrejaguar em 08 de May de 2016 às 09:25

May 06, 2016

PythonClub

Python com Unittest, Travis CI, Coveralls e Landscape (Parte 1 de 4)

Durante o desenvolvimento de um software, tão importante quanto escrever um código organizado e que siga as melhores práticas, é garantir que o mesmo cumpra os requisitos a que ele se propõe. Em outras palavras, garantir que o software funcione de maneira adequada.

O processo de testes de um software faz parte do seu desenvolvimento, porém muitas vezes ele é encarado como algo tedioso e desnecessário. Entretanto, todo bom desenvolvedor sabe que investir tempo escrevendo testes para seu software está longe de ser "desperdício de tempo". O processo de teste, quando feito por uma pessoa, além de sujeitos a falhas é tedioso e demorado. Tendo isso em mente, podemos lançar mão de ferramentas que realizarão o processo de teste por nós. Em Python, umas das ferramentes da bibloteca padrão destinada a teste é a Unittest, que usaremos nesse tutorial.

Nesta série de postagem, irei mostrar o passo-a-passo na criação de testes unitários para um pequeno projeto que vamos criar no github. Vou explicar como configurar a ferramenta Travis, que será responsável por executar os nossos testes no github. A ferramenta Coveralls, que mapeia nosso código, e nos indica o quanto dele está coberto por testes. E como bônus, adicionar ao nosso projeto o Landscape, ferramenta que monitora a "saúde" do nosso código.

Iniciando nosso projeto

Inicialmente, criei no github um repositório que vai receber meu código e que posteriormente será configurado para rodar nossos testes. No meu caso, o repositório foi esse codigo-avulso-test-tutorial. Após realizar o clone, criei a seguite estrutura de diretorios:

.
├── codigo_avulso_test_tutorial
│   └── __init__.py
├── LICENSE
├── README.md
└── test
    └── __init__.py

O diretório codigo_avulso_test_tutorial receberá o código da nossa aplicação e o diretório test receberá o código de teste. O nosso projeto consiste de um grupo de classes representando figuras geométricas (quadrados, círculos e etc). Teremos uma classe base chamada FiguraGeometrica que possui dois métodos, a saber: get_area e get_perimetro, sendo ambos metódos abstratos. Cada uma dessas classes filhas de FiguraGeometrica irá possuir sua própria implementação desses métodos.

Dentro do diretório codigo_avulso_test_tutorial, irei criar os fontes do nosso código:

touch figura_geometrica.py circulo.py quadrado.py

Dentro do diretório test, irei criar os fontes do nosso código de teste:

touch figura_geometrica_test.py circulo_test.py quadrado_test.py

Uma observação importante é que os arquivos de teste devem ter o nome terminado em test, para que o módulo de Unittest encontre os nossos arquivos de teste automaticamente. Após a criação dos arquivos, teremos a seguinte estrutura de diretório:

.
├── codigo_avulso_test_tutorial
│   ├── circulo.py
│   ├── figura_geometrica.py
│   ├── __init__.py
│   └── quadrado.py
├── LICENSE
├── README.md
└── test
    ├── circulo_test.py
    ├── figura_geometrica_test.py
    ├── __init__.py
    └── quadrado_test.py

Iniciemos agora a implementação do nosso projeto. Mas antes vamos dar uma olhada em alguns conceitos.

Test Driven Development (TDD)

Neste momento, leitor, você deve estar se perguntando: "Não deveríamos escrever primeiro o nosso código e depois escrever os testes?".

Não necessariamente. O processo de escrever os testes antes do código é chamado de TDD - Test Driven Development. Segundo a wikipedia:

"Test Driven Development (TDD) ou em português Desenvolvimento guiado por testes é uma técnica de desenvolvimento de software que baseia em um ciclo curto de repetições: Primeiramente o desenvolvedor escreve um caso de teste automatizado que define uma melhoria desejada ou uma nova funcionalidade. Então, é produzido código que possa ser validado pelo teste para posteriormente o código ser refatorado para um código sob padrões aceitáveis. Kent Beck, considerado o criador ou o 'descobridor' da técnica, declarou em 2003 que TDD encoraja designs de código simples e inspira confiança[1] . Desenvolvimento dirigido por testes é relacionado a conceitos de programação de Extreme Programming, iniciado em 1999,[2] mas recentemente tem-se criado maior interesse pela mesma em função de seus próprios ideais.[3] Através de TDD, programadores podem aplicar o conceito de melhorar e depurar código legado desenvolvido a partir de técnicas antigas.[4]"

Criando o setup.py

Antes de começar a implementar o códigos de teste, vamos criar o arquivo setup.py. Esse arquivo contém informações sobr e o nosso módulo python e facilita em muito a utilização dos testes. Então, vamos criar o arquivo setup.py na pasta raiz do nosso projeto.

touch setup.py

A estrutura do nosso projeto agora está assim:

.
├── codigo_avulso_test_tutorial
│   ├── circulo.py
│   ├── figura_geometrica.py
│   ├── __init__.py
│   └── quadrado.py
├── LICENSE
├── README.md
├── setup.py
└── test
    ├── circulo_test.py
    ├── figura_geometrica_test.py
    ├── __init__.py
    └── quadrado_test.py

Abra o setup.py em um editor e adicione as informações conforme exemplo abaixo:

# -*- coding: utf-8 -*-
from setuptools import setup
setup(
    name='codigo-avulso-test-tutorial',
    packages=['codigo_avulso_test_tutorial', 'test'],
    test_suite='test',
)

No código acima, name representa o nome do seu projeto, packages são os diretórios do seu projeto que possuem código fonte e test_suite indica o diretório onde estão os fontes de teste. É importante declarar esse diretório pois o Unittest irá procurar dentro dele os arquivos de teste que iremos escrever.

Criando testes para a classe FiguraGeometrica

Agora, vamos usar a lógica do TDD. Primeiro criamos o código de teste de uma classe para em seguida criamos o código da mesma. Das classes que criamos, o arquivo figura_geometrica.py servirá como uma classe base para as outras classes. Então vamos começar por elá.

Abra o arquivo figura_geometrica_test.py e seu editor preferido e adicione o código abaixo:

# -*- coding: utf-8 -*-
from unittest import TestCase
from codigo_avulso_test_tutorial.figura_geometrica import FiguraGeometrica

# O nome da classe deve iniciar com a palavra Test
class TestFiguraGeometrico(TestCase):

    # Serve para incializar variavei que usaremos
    # globalmente nos testes
    def setUp(self):
        TestCase.setUp(self)
        self.fig = FiguraGeometrica()

    # Retorna uma NotImplementedError
    # O nome do metodo deve comecar com test
    def test_get_area(self):
        self.assertRaises(NotImplementedError, self.fig.get_area)

    # Retorna uma NotImplementedError
    # O nome do metodo deve comecar com test
    def test_get_perimetro(self):
        self.assertRaises(NotImplementedError, self.fig.get_perimetro)

Como podemos observar no código acima, a seguinte linha:

def test_get_area(self):
    self.assertRaise(self.fig.test_get_area(), NotImplementedError)

Realiza o seguinte teste. Com o objeto self.fig criado no método setUp(), tentamos chamar o método test_get_perimetro da classe FiguraGeometrica, porém ele verifica se ocorreu a exceção NotImplementedError. Isso é feito porque a classe FiguraGeometrica é uma classe abstrata e possui ambos os métodos get_area e get_perimetro vazios. Isso irá ficar mais claro quando adicionarmos o código da classe FiguraGeometrica. Então, abra o arquivo figura_geometrica.py em seu editor e vamos adicionar o seguinte código:

# -*- coding: utf-8 -*-

class FiguraGeometrica(object):

    # Retorna a area da figura
    def get_area(self):
        raise NotImplementedError

    # Retorna o perimetro da figura
    def get_perimetro(self):
        raise NotImplementedError

A class acima é bem simples. Ela possui um método que retorna a área e outro que retorna o perímetro da figura. Ambos são métodos abstratos, ou seja, devem ser implementados nas classes filhas da classe FiguraGeometrica. Se criarmos um objeto dessa classe e chamarmos um dos dois métodos, uma exceção do tipo NotImplementedError será lançada, pois ambos os métodos possuem escopo vazio.

Finalmente podemos executar o teste da nossa classe. Usando o terminal, no diretorio em que o arquivo setup.py está, execute o seguinte comando:

python setup.py test

Esse nosso comando vai executar a nossa classe TestFiguraGeometrica. Se tudo estiver correto, teremos a seguinte saída:

running test
running egg_info
writing codigo_avulso_test_tutorial.egg-info/PKG-INFO
writing top-level names to codigo_avulso_test_tutorial.egg-info/top_level.txt
writing dependency_links to codigo_avulso_test_tutorial.egg-info/dependency_links.txt
reading manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
writing manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
running build_ext
test_get_area (test.figura_geometrica_test.TestFiguraGeometrico) ... ok
test_get_perimetro (test.figura_geometrica_test.TestFiguraGeometrico) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Caso apareça uma resposta diferente, dê uma olhada na própria saída do teste. Ele indica onde está o erro. Provavelmente, pode ter sido algum erro de digitação, pois os exemplos deste tutorial foram todos testados.

Criando testes para a classe Quadrado

Vamos criar agora outras classes que realmente fazem algo de útil e seus respectivos testes. Começando pela classe Quadrado, vamos escrever um teste para a mesma no arquivo quadrado_test.py.

# -*- coding: utf-8 -*-

from unittest import TestCase
from codigo_avulso_test_tutorial.quadrado import Quadrado

class TestQuadrado(TestCase):

    def setUp(self):
        TestCase.setUp(self)
        self.fig = Quadrado()

    def test_get_area(self):
        # Verificamos se o resultado é o esperado
        # de acordo com a formula de area do quadrado
        self.fig.lado = 2
        self.assertEqual(self.fig.get_area(), 4)
        self.fig.lado = 7.0
        self.assertEqual(self.fig.get_area(), 49.0)

    def test_get_perimetro(self):
        self.fig.lado = 2
        self.assertEqual(self.fig.get_perimetro(), 8)
        self.fig.lado = 7.0
        self.assertEqual(self.fig.get_perimetro(), 28.0)

Em seguida, adicionamos o código da classe Quadrado no arquivo quadrado.py:

# -*- coding: utf-8 -*-

from figura_geometrica import FiguraGeometrica

class Quadrado(FiguraGeometrica):

    def __init__(self):
      self.lado = 0

    # Retorna a area do quadrado
    def get_area(self):
        return self.lado**2

    # Retorna o perimetro do quadrado
    def get_perimetro(self):
        return 4 * self.lado

Assim como fizemos no exemplo anterior, executamos os testes:

python setup.py test

Se tudo estiver certo, teremos a seguinte saída.

running test
running egg_info
writing codigo_avulso_test_tutorial.egg-info/PKG-INFO
writing top-level names to codigo_avulso_test_tutorial.egg-info/top_level.txt
writing dependency_links to codigo_avulso_test_tutorial.egg-info/dependency_links.txt
reading manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
writing manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
running build_ext
test_get_area (test.quadrado_test.TestQuadrado) ... ok
test_get_perimetro (test.quadrado_test.TestQuadrado) ... ok
test_get_area (test.figura_geometrica_test.TestFiguraGeometrico) ... ok
test_get_perimetro (test.figura_geometrica_test.TestFiguraGeometrico) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

Uma detalhe interessante a ser observado é que agora os testes da classe Quadrado estão sendo executados junto com os testes da classe FiguraGeometrica sem que fosse necessário alterar nenhuma configuração do projeto, ou adicionar algum novo diretório no arquivo setup.py. Isso acontece por que usamos a sufixo _test no nome dos nossos código fonte de teste e também adicionamos o diretório test na tag test_suite no arquivo setup.py. Desse modo, quando executamos os testes, o módulo python Unittest percorre o diretório test, carrega automaticamente todos os arquivos com sufixo _test e executa os testes dentro deles. Bacana não é?

Criando testes para a classe Circulo

Para encerrarmos o tutorial, vamos agora implementar os testes da classe Círculo.

# -*- coding: utf-8 -*-
import math
from unittest import TestCase
from codigo_avulso_test_tutorial.circulo import Circulo

class TestCirculo(TestCase):

    def setUp(self):
        TestCase.setUp(self)
        self.fig = Circulo()

    def test_get_area(self):
        # Utilizamos a formula diretamente por conveniencia
        # já que math.pi e double e sendo assim, possui
        # muitas casas decimais
        self.fig.raio = 2
        area = math.pi * self.fig.raio**2
        self.assertEqual(self.fig.get_area(), area)

        self.fig.raio = 7.0
        area = math.pi * self.fig.raio**2
        self.assertEqual(self.fig.get_area(), area)

    def test_get_perimetro(self):
        self.fig.raio = 2
        perimetro = 2 * math.pi * self.fig.raio
        self.assertEqual(self.fig.get_perimetro(), perimetro)

        self.fig.raio = 7.0
        perimetro = 2 * math.pi * self.fig.raio
        self.assertEqual(self.fig.get_perimetro(), perimetro)

E agora a classe Circulo:

# -*- coding: utf-8 -*-
import math
from figura_geometrica import FiguraGeometrica

class Circulo(FiguraGeometrica):

    def __init__(self):
      self.raio = 0

    # Retorna a area do circulo
    def get_area(self):
        return math.pi * self.raio**2

    # Retorna o perimetro do circulo
    def get_perimetro(self):
        return 2 * math.pi * self.raio

Finalmente, rodamos os testes agora com a presença da classe circúlo:

python setup.py test

Se tudo estiver certo, teremos a seguinte saída.

running test
running egg_info
writing codigo_avulso_test_tutorial.egg-info/PKG-INFO
writing top-level names to codigo_avulso_test_tutorial.egg-info/top_level.txt
writing dependency_links to codigo_avulso_test_tutorial.egg-info/dependency_links.txt
reading manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
writing manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
running build_ext
test_get_area (test.quadrado_test.TestQuadrado) ... ok
test_get_perimetro (test.quadrado_test.TestQuadrado) ... ok
test_get_area (test.figura_geometrica_test.TestFiguraGeometrico) ... ok
test_get_perimetro (test.figura_geometrica_test.TestFiguraGeometrico) ... ok
test_get_area (test.circulo_test.TestCirculo) ... ok
test_get_perimetro (test.circulo_test.TestCirculo) ... ok

----------------------------------------------------------------------
Ran 6 tests in 0.001s

OK

Conclusão

Com os testes ok, só nos resta subir o código para o github:

git add --all
git commit -m "[NEW] Adicionado classes e testes"
git push origin master

Esse tutorial ficou bem extenso, mas espero que tenha sido útil pra vocês. No próxima parte do tutorial, vamos ver como configurar o Travis, para que ele execute nossos testes quando realizarmos um push ou um pull request para o github. Também veremos o Coveralls que emite relatórios do quando do seu código está coberto por testes, algo muito interessante para ver se um software é bem testado.

Os testes que escrevemos foram bem simples, apenas para fim de exemplo. Porém em uma aplicação séria, deve-se ter cuidado na hora de escrever os testes, de maneira a garantir que todas as possibilidades de erros sejam cobertas. A filosofia do TDD de escrever os testes antes do código da nossa aplicação, é algo que exige prática. Eu mesmo ainda não me sinto completamente a vontade seguindo esse fluxo de trabalho. Mas, escrever os testes primeiro te ajuda a manter seu código coerente e funcional, pois vocẽ vai precisar fazê-lo passar pelos testes.

É isso pessoal. Obrigado por ler até aqui. Até a próxima postagem!

Publicado originalmente: python-com-unittest-travis-ci-coveralls-e-landscape-parte-1-de-4

por Michell Stuttgart em 06 de May de 2016 às 04:42

May 05, 2016

PythonClub

GitHub Pages com Pelican e Travis-CI

Publicado originalmente em: df.python.org.br/blog/github-pages-com-pelican-e-travis-ci

Olá pessoal!

Estou fazendo esta postagem para ajudar quem quer criar seu site no GitHub Pages usando Pelican para a criação das páginas e o Travis-CI para automatizar a tarefa de geração e publicação.

Este guia assume que o leitor possua conta no GitHub e no Travis-CI e tenha familiaridade com o ambiente python. A versão do pelican utilizada ao elaborar esta publicação foi a 3.6.

O GitHub Pages

O GitHub Pages é uma funcionalidade gratuita do GitHub para hospedar conteúdo estático (html, css, js e imagens) e publicar através de um sub-domínio de github.io ou até mesmo de um domínio customizado. É baseado em seu funcionamento que iremos guiar nossos próximos passos.

Resumidamente existem duas formas de se criar uma página pelo gh pages:

1 - Página de usuário/organização

Para este tipo de página crie um repositório com o nome usuario.github.io, onde usuario é o nome de usuário ou organização da conta em que o repositório será criado:

Repositório da página de usuário

O conteúdo a ser publicado deve ser colocado na branch master e os arquivos do pelican na branch pelican.

2 - Página de projeto

Para este tipo de página crie um repositório com o nome meuprojeto, onde meuprojeto é o nome desejado para o projeto que será publicado em usuario.github.io:

Repositório da página de projeto

O conteúdo a ser publicado deve ser colocado na branch gh-pages e os arquivos do pelican na branch pelican.

Para mais informações acesse o site oficial do GitHub Pages.

Pelican

O pelican é um gerador de site estático otimizado por padrão para criação de blogs. Utilizaremos aqui, para fins de demonstração, o modelo padrão do de blog seguindo o caminho de criação de página de usuário/organização, qualquer diferença do caminho de página de projeto será descrita quando necessário.

Para instalar o pelican basta rodar o comando:

$ pip install pelican==3.6

Para criar um projeto faça:

$ mkdir humrochagf.github.io
$ cd humrochagf.github.io
$ pelican-quickstart
Welcome to pelican-quickstart v3.6.3.

This script will help you create a new Pelican-based website.

Please answer the following questions so this script can generate the files
needed by Pelican.


> Where do you want to create your new web site? [.]
> What will be the title of this web site? Meu Blog
> Who will be the author of this web site? Humberto Rocha
> What will be the default language of this web site? [en] pt
> Do you want to specify a URL prefix? e.g., http://example.com   (Y/n) n
> Do you want to enable article pagination? (Y/n) y
> How many articles per page do you want? [10]
> What is your time zone? [Europe/Paris] America/Sao_Paulo
> Do you want to generate a Fabfile/Makefile to automate generation and publishing? (Y/n) y
> Do you want an auto-reload & simpleHTTP script to assist with theme and site development? (Y/n) y
> Do you want to upload your website using FTP? (y/N) n
> Do you want to upload your website using SSH? (y/N) n
> Do you want to upload your website using Dropbox? (y/N) n
> Do you want to upload your website using S3? (y/N) n
> Do you want to upload your website using Rackspace Cloud Files? (y/N) n
> Do you want to upload your website using GitHub Pages? (y/N) y
> Is this your personal page (username.github.io)? (y/N) y
Done. Your new project is available at /caminho/para/humrochagf.github.io

Inicialize um repositório neste diretório e suba os dados para a branch pelican:

$ git init
$ git remote add origin git@github.com:humrochagf/humrochagf.github.io.git
$ git checkout -b pelican
$ git add .
$ git commit -m 'iniciando branch pelican'
$ git push origin pelican

Para publicar o conteúdo na branch master é necessário o módulo ghp-import:

$ pip install ghp-import
$ echo 'pelican==3.6\nghp-import' > requirements.txt
$ git add requirements.txt
$ git commit -m 'adicionando requirements'
$ git push origin pelican

Publicando o blog:

$ make github
Primeira publicação do blog

Para publicar no caso da página de projeto altere o conteúdo da variável GITHUB_PAGES_BRANCH do makefile de master para gh-pages.

Agora que o nosso blog está rodando no gh pages vamos automatizar a tarefa de geração das páginas para poder alterar o conteúdo do blog e fazer novas postagens sem precisar estar um uma máquina com o ambiente do pelican configurado.

Travis-CI

O Travis-CI é uma plataforma de Integração Contínua que monta e testa projetos hospedados no github e será nossa ferramenta para automatizar a montagem das páginas do blog.

A Primeira coisa a ser feita é ir ao Travis-CI e habilitar seu repositório.

Habilitando repositório no travis

Em seguida vá nas configurações do repositório no travis e desabilite a opção Build pull requests para seu blog não ser atualizado quando alguém abrir um pull request e habilite o Build only if .travis.yml is present para que somente a branch que possuir o arquivo .travis.yml gerar atualização no blog.

Configurando remositório no travis

O próximo passo é criar uma Deploy Key para que o travis possa publicar conteúdo no github. Para isso gere uma chave ssh na raiz do repositório local:

$ ssh-keygen -f publish-key
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in publish-key.
Your public key has been saved in publish-key.pub.

Criada a chave vamos cifrar usando a ferramenta Travis-CLI (certifique-se de que esteja instalada em sua máquina) para poder publicar em nosso repositório sem expor o conteúdo da chave privada:

$ travis encrypt-file publish-key
Detected repository as humrochagf/humrochagf.github.io, is this correct? |yes| yes
encrypting publish-key for humrochagf/humrochagf.github.io
storing result as publish-key.enc
storing secure env variables for decryption

Please add the following to your build script (before_install stage in your .travis.yml, for instance):

    openssl aes-256-cbc -K $encrypted_591fe46d4973_key -iv $encrypted_591fe46d4973_iv -in publish-key.enc -out publish-key -d

Pro Tip: You can add it automatically by running with --add.

Make sure to add publish-key.enc to the git repository.
Make sure not to add publish-key to the git repository.
Commit all changes to your .travis.yml.

Como dito no resultado do comando podemos adicionar a opção --add para já adicionar as informações no .travis.yml, porém, para evitar de sobrescrever algum comando que venha existir no seu arquivo é recomendado editar manualmente.

Em nosso caso iremos criar o arquivo:

$ touch .travis.yml

E adicionar o seguinte conteúdo:

sudo: false
branches:
  only:
  - pelican
language: python
before_install:
# troque a linha abaixo pelo resultado do comando:
# travis encrypt-file publish-key
# porém mantenha o final:
# -out ~/.ssh/publish-key -d
- openssl aes-256-cbc -K $encrypted_591fe46d4973_key -iv $encrypted_591fe46d4973_iv -in publish-key.enc -out ~/.ssh/publish-key -d
- chmod u=rw,og= ~/.ssh/publish-key
- echo "Host github.com" >> ~/.ssh/config
- echo "  IdentityFile ~/.ssh/publish-key" >> ~/.ssh/config
# substitua git@github.com:humrochagf/humrochagf.github.io.git
# pelo endereço de acesso ssh do seu repositório
- git remote set-url origin git@github.com:humrochagf/humrochagf.github.io.git
# Caso esteja montando a página de projeto troque master:master
# por gh-pages:gh-pages
- git fetch origin -f master:master
install:
- pip install --upgrade pip
- pip install -r requirements.txt
script:
- make github

Removemos em seguida a chave privada não cifrada para não correr o risco de publicar no repositório:

$ rm publish-key

ATENÇÃO: Em hipótese alguma adicione o arquivo publish-key em seu repositório, pois ele contém a chave privada não cifrada que tem poder de commit em seu repositório, e não deve ser publicada. Adicione somente o arquivo publish-key.enc. Se você adicionou por engano refaça os passos de geração da chave e cifração para gerar uma chave nova.

Agora adicionaremos os arquivos no repositório:

$ git add .travis.yml publish-key.enc
$ git commit -m 'adicionando arquivos do travis'
$ git push origin pelican

Para liberar o acesso do travis adicionaremos a deploy key no github com o conteúdo da chave pública publish-key.pub:

Adicionando a deploy key no github

Pronto, agora podemos publicar conteúdo em nosso blog sem a necessidade de ter o pelican instalado na máquina:

Fazendo a primeira postagem

Que o travis irá publicar para você:

Blog com a primeira postagem

Caso você tenha animado de criar seu blog pessoal e quer saber mais sobre pelican você pode acompanhar a série do Mind Bending sobre o assunto.

por Humberto Rocha em 05 de May de 2016 às 00:46

May 02, 2016

Álvaro Jüsten

Dados Tabulares: A Maneira Pythônica

Já venho há algum tempo trabalhando na biblitoeca rows, que facilita demais o acesso a dados tabulares, não importa o formato do arquivo (CSV, XLS, XLSX, HTML, dentre outros). No último sábado (30 de abril) fiz uma palestra no encontro PythOnRio sobre o projeto e a galera se amarrou! Além da biblioteca, também mostrei como usar a command-line interface, que uso diariamente para pequenas análises e conversão de dados.

Confira o vídeo:

E os slides:

Veja também a versão em HTML dos slides.

O projeto é software livre e temos várias issues abertas, entra lá no repositório no GitHub e contribui! :)

por noreply@blogger.com (Álvaro Justen "Turicas") em 02 de May de 2016 às 16:27

Christiano Anderson

Treinamento de MongoDB presencial em Porto Alegre

Treinamento MongoDB

Treinamento MongoDB

O tão esperado treinamento de MongoDB acabou de sair do forno! Foram meses de trabalho para elaborar toda didática, material e exercícios. Finalmente deu certo e abrimos a primeira turma para o fim de maio.

O treinamento tem um total de 40 horas, é totalmente mão na massa e o principal objetivo é formar especialistas em MongoDB, com um enfoque em desenvolvimento e data science. Para desenvolver esse treinamento, aproveitei as melhores experiências que tive como consultor de MongoDB nos últimos anos, foram diversas implementações em clientes como Easy Taxi, Petrobras, Mercado Livre, entre outros.

Percebi que o mercado procurava não apenas um treinamento simples de MongoDB, mas que ajudasse a pensar de forma não relacional, já que schema design é o maior ponto de falha nessas implementações. Juntei um pouco de Python (para mostrar como desenvolver usando MongoDB) e algumas pitadas de data science, algo que tenho trabalhado diariamente.

Embora os exemplos sejam em Python, o mesmo pode ser aplicado a qualquer linguagem de programação.

Com tudo isso, esse treinamento não é apenas para formar novos ninjas em MongoDB, mas formar profissionais aptos a uma nova realidade que exige arquitetura de dados, desenvolvimento e data science.

Depois de Porto Alegre, vamos lançar esse mesmo treinamento em outras cidades!

Confira toda programação em http://even.tc/curso-mongodb e faça já sua inscrição, são poucas vagas!

Faça sua inscrição!!

Conteúdo programático

  • Módulo conceitual
    • Bancos não relacionais
    • Teorema de CAP
    • Outros bancos não relacionais
    • O que é MongoDB
    • História
    • Como pensar de forma Não Relacional
    • Onde usar NoSQL
    • Onde não usar NoSQL
  • Módulo introdutório
    • Instalação do MongoDB
    • O conceito de documentos
    • Tipos de instalação
    • Demonstração modo Standalone
    • Demonstração modo Réplica Set
    • Demonstração modo Sharding
    • Definições (Database, Coleção, Documentos)
  • Módulo CRUD
    • Inserindo documentos
    • Consultando documentos
    • Atualizando documentos
    • Removendo documentos
  • Módulo filtros avançados
    • Operadores de consulta
    • Consultando subdocumentos
    • Arrays
    • Filtros avançados
  • Módulo índices
    • Índices básicos
    • Índices compostos
    • Índices textuais
    • Índices geográficos
    • Índices com expire (TTL)
  • Módulo Aggregation Framework
    • Operador de pipeline
    • Operador $group
    • Operador $match
    • Operador $unwind
    • Demais operadores
    • Exemplos de uso
  • Módulo Replica Set
    • Criando um réplica set
    • Conceito de primário, secundário, secundário
    • Topologia recomendada
    • O uso ou não de árbitro
    • Limitações
  • Módulo Sharding
    • Criando um cluster
    • Usando réplica sets
    • Componentes do sharding
    • Escolhendo uma shard key corretamente
    • Tipos de shard key
    • Limitações
    • Importação de um grande volume de dados
  • Módulo desenvolvimento
    • Estabelecendo conexão com Python
    • Inserindo documentos em Python
    • Consultado documentos em Python
    • Atualizando documentos em Python
    • Removendo documentos em Python
    • Exercícios práticos
  • Módulo Arquitetura
    • Estruturando documentos
    • Limitações
    • Normalização vs Desnormalização
    • Erros mais comuns
    • O que não fazer
    • Boas práticas
    • Analisando um sistema de locadora
  • Módulo administração
    • Boas práticas no servidor de produção
    • Tuning
    • Como identificar gargalos
    • Analisando índices
    • Analisando performance geral do servidor
    • Autenticação e autorização
  • Módulo monitoramento
    • Usando o Cloud Manager
    • Como monitorar com Nagios
  • Módulo backup
    • Estratégias de backup
    • Estratégias de restore
  • Módulo bônus
    • Usando MongoDB e banco relacional na mesma aplicação
    • Outros bancos não relacionais
    • Dicas de como utilizar MongoDB em ambiente Amazon AWS
    • Dicas de data science, como processar dados usando MongoDB;

Para informações sobre locais, datas e condições, acesse o link abaixo:

Faça sua inscrição!!

O post Treinamento de MongoDB presencial em Porto Alegre apareceu primeiro em Christiano Anderson.

por Christiano Anderson em 02 de May de 2016 às 13:50

April 30, 2016

PythonClub

Sites Estáticos com Lektor

Publicado originalmente em: humberto.io/2016/4/sites-estaticos-com-lektor

Faz pelo menos 4 anos que eu ensaio para montar um blog, e nessa brincadeira já montei alguns, mas quando chegava na hora de criar o meu eu nunca conseguia publicar.

Inicialmente com ferramentas de publicação como wordpress o problema era a dificuldade de customizar e o tanto de coisa que vinha junto que eu não ia usar mas ficava me tirando a atenção. Em seguida com o GitHub Pages eu descobri o Pelican por indicação do Magnun Leno e comecei a fazer muita coisa com ele, mas da mesma forma que eu ganhei em liberdade de customização, o processo autoral é o mesmo de desenvolvimento, e como descrito no subtitulo do blog, meu lado cientista, pythonista e curioso ficava ali me cutucando para melhorar o site ao invés de escrever conteúdo.

Eis que em uma conversa no grupo de telegram da comunidade python me citam o Lektor e aí começou a aventura.

Lektor?

Lektor é um gerenciador de conteúdo estático criado por Armin Ronacher (sim, o criador do flask) que permite a criação de websites a partir de arquivos de texto.

Porque usar?

Como descrito no próprio site ele bebeu das fontes dos CMS`s, dos frameworks e dos geradores de site estático e chegou em algo que eu considero um ponto de equilíbrio entre eles, e que nos leva as seguintes vantagens:

  • Estático: O site final é totalmente estático, o que permite sua hospedagem em qualquer lugar;
  • CMS: Uma interface de produção de conteúdo que roda localmente e tira a necessidade de entender programação para poder produzir conteúdo. (no meu caso me tira do mundo do código e me deixa focar no conteúdo);
  • Framework: Ele possuí um sistema de models em arquivos de texto e um sistema de templates que usa Jinja2 que cria um ambiente familiar para quem já desenvolveu algo em django, flask e similares;
  • Deploy: O sistema de deploy dele é reduzido á uma configuração em um arquivo, o que permite a rápida publicação sem ficar dias aprendendo técnicas de deploy quando tudo que você quer no começo é colocar seu site no ar.

Instalação

A instalação do Lektor é bem direta:

$ curl -sf https://www.getlektor.com/install.sh | sh

Este comando instala diretamente no sistema, se você prefere instalar em sua virtualenv:

$ virtualenv venv
$ . venv/bin/activate
$ pip install Lektor

Esta forma é desencorajada pelos desenvolvedores pois o lektor gerencia virtualenvs internamente para instalação de seus plugins, portanto caso seja desenvolvedor e quer ter mais controle sobre o lektor instale a versão de desenvolvimento e esteja pronto para sujar as mãos quando for preciso, e quem sabe até contribuir com o desenvolvimento do lektor:

$ git clone https://github.com/lektor/lektor
$ cd lektor
$ make build-js
$ virtualenv venv
$ . venv/bin/activate
$ pip install --editable .

Obs.: requer npm instalado para montar a interface de administração.

Criando o Site

Após a instalação para criar o seu site basta utilizar o comando de criação de projeto:

$ lektor quickstart

Ele irá te fazer algumas perguntas e criar um projeto com o nome que você informou.

Estrutura

Esta é a estrutura básica de um site gerado pelo lektor:

meusite
├── assets/
├── content/
├── templates/
├── models/
└── meusite.lektorproject
  • assets: Pasta onde ficam os arquivos .css, .js, .ico entre outros recursos estáticos;
  • content: Pasta onde ficam os arquivos que iram gerar as páginas do site, cada subpasta corresponde a uma página no site gerado;
  • templates: Pasta onde ficam os arquivos de template que definem a sua estrutura visual;
  • models: Pasta onde ficam os arquivos que definem a modelagem de dados;
  • meusite.lektorproject: Arquivo com as configurações gerais do site.

Executando localmente

Para rodar o site em sua máquina basta entrar no diretório criado e iniciar o servidor local:

$ cd meusite
$ lektor server

Com o servidor rodando acesse localhost:5000 para ver o resultado:

meusite

Acessando o Admin

Para acessar o admin clique na imagem de lápis no canto superior direito da página que você criou ou acesse localhost:5000

meusite-admin

Publicando o Site

Exitem duas maneiras de se fazer o deploy do site construído com o lektor, a manual, que é basicamente rodar o comando build e copiar manualmente os arquivos para o servidor:

$ lektor build --output-path destino

E a forma automática, que pode ser feita (neste caso para o GitHub Pages) adicionando a seguinte configuração no arquivo meusite.lektorproject:

[servers.production]
target = ghpages://usuario/repositorio

E rodando em seguida o comando:

$ lektor deploy

Obs.: O deploy faz um force push na branch master ou gh-pages dependendo do tipo de repositório, portanto, cuidado para não sobrescrever os dados de seu repositório. Mantenha o código fonte em uma branch separada, você pode dar uma conferida no meu repositório para ter uma idéia.

Para informações mais detalhadas você pode acessar a documentação do lektor e também ficar de olho nas próximas postagens.

por Humberto Rocha em 30 de April de 2016 às 15:00

April 24, 2016

PythonClub

Django Rest Framework Serialization

Eu resolvi estudar um pouco mais de DRF depois do tutorial do Hugo Brilhante na Python Brasil 11.

Obs: se você não sabe Django sugiro que leia este tutorial antes.

Este tutorial é a parte 1/6 de uma série de posts sobre DRF.

Obs: Tem coisas que é melhor nem traduzir. ;)

  • 0 - Quickstart
  • 1 - Serialization
  • 2 - Requests & Responses
  • 3 - Class based views
  • 4 - Authentication & permissions
  • 5 - Relationships & hyperlinked APIs
  • 6 - Viewsets & routers

Pra quem não sabe, para usar API Web usamos REST, no caso, Django Rest Framework, framework web do Django.

Nota: este tutorial não é exatamente igual ao do Hugo, é baseado nele. E baseado também em Tutorial 1: Serialization.

Então para criar a API, no meu caso, eu usei:

  • Ambiente: .venv
  • Projeto: myproject
  • App: core
  • Model: Person
  • Fields: first_name, last_name, email, active (boolean), created

img

Configurando um novo ambiente

$ virtualenv -p python3 .venv
$ source .venv/bin/activate
$ mkdir drf; cd drf
$ pip install django==1.9.5 djangorestframework==3.3.3
$ pip install django-filter drf-nested-routers
$ pip freeze > requirements.txt
$ django-admin.py startproject myproject .
$ python manage.py startapp core

Veja o meu requirements.txt

Django==1.9.5
django-filter==0.11.0
djangorestframework==3.3.3
drf-nested-routers==0.10.0

Step-0 Projeto inicial

Abra o arquivo settings.py e em INSTALLED_APPS acrescente

INSTALLED_APPS = (
    ...
    'rest_framework',
    'core',
)

Step-1 Serializer

models.py: Criando o modelo Person

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField(null=True, blank=True)
    active = models.BooleanField(default=True)
    created = models.DateTimeField(auto_now_add=True, auto_now=False)

    class Meta:
        ordering = ['first_name']
        verbose_name = u'pessoa'
        verbose_name_plural = u'pessoas'

    def __str__(self):
        return self.first_name + " " + self.last_name

    full_name = property(__str__)

serializers.py: Criando PersonSerializer

Precisamos proporcionar uma forma de serialização e desserialização das instâncias de person em uma representação JSON.

$ cd core/
$ touch serializers.py

Edite

from rest_framework import serializers
from core.models import Person

class PersonSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    first_name = serializers.CharField(max_length=30)
    last_name = serializers.CharField(max_length=30)
    email = serializers.EmailField()
    active = serializers.BooleanField(default=True)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        """
        Create and return a new `Person` instance, given the validated data.
        :param validated_data:
        """
        return Person.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Person` instance, given the validated data.
        """

        instance.first_name = validated_data.get(
            'first_name', instance.first_name)
        instance.last_name = validated_data.get(
            'last_name', instance.last_name)
        instance.email = validated_data.get('email', instance.email)
        instance.save()
        return instance

A primeira parte da classe define os campos que serão serializados. Os métodos create() e update() criam e atualizam as instâncias, respectivamente, quando chamados.

Uma classe de serialização é similar a uma classe Form do Django, e inclui validações similares para os campos, tais como required, max_length e default.

Fazendo a migração

$ ./manage.py makemigrations core
$ ./manage.py migrate

Trabalhando com a serialização

Abra o shell do Django.

$ ./manage.py shell

Primeiro vamos criar uma pessoa.

>>> from core.models import Person
>>> from core.serializers import PersonSerializer
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser

>>> person = Person(first_name='Paul', last_name='Van Dyke', email='paul@email.com')
>>> person.save()

>>> person = Person(first_name='Regis', last_name='Santos', email='regis@email.com')
>>> person.save()

Agora que já temos alguns dados podemos ver a serialização da última instância.

>>> serializer = PersonSerializer(person)
>>> serializer.data
# {'pk': 2, 'first_name': 'Regis', 'created': '2015-11-15T03:20:25.084990Z', 'last_name': 'Santos', 'email': 'regis@email.com', 'active': True}

Neste ponto nós traduzimos a instância do modelo em tipos de dados nativos do Python. Para finalizar o processo de serialização nós vamos renderizar os dados em json.

>>> content = JSONRenderer().render(serializer.data)
>>> content
# b'{"pk":2,"first_name":"Regis","last_name":"Santos","email":"regis@email.com","active":true,"created":"2015-11-15T03:20:25.084990Z"}'

A desserialização é similar.

>>> from core.models import Person
>>> from core.serializers import PersonSerializer
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser
>>> from django.utils.six import BytesIO

>>> person = Person.objects.get(pk=1)
>>> serializer = PersonSerializer(person)
>>> content = JSONRenderer().render(serializer.data)
>>> stream = BytesIO(content)
>>> data = JSONParser().parse(stream)
>>> serializer = PersonSerializer(data=data)
>>> serializer.is_valid()
# True
>>> serializer.validated_data
# OrderedDict([('first_name', 'Paul'), ('last_name', 'Van Dyke'), ('email', 'paul@email.com'), ('active', True)])

Step-2 ModelSerializer

Nossa classe PersonSerializer está replicando um monte de informações que está contido no modelo Person.

Da mesma forma que o Django fornece Form e ModelForm, REST framework inclui as classes Serializer e ModelSerializer.

Vamos refatorar nosso arquivo serializers.py, que agora ficará assim:

from rest_framework import serializers
from core.models import Person

class PersonSerializer(serializers.ModelSerializer):

    class Meta:
        model = Person
        fields = ('pk', 'first_name', 'last_name','email', 'active', 'created')

Uma propriedade legal que a serialização tem é que você pode inspecionar todos os campos em uma instância serializer, imprimindo sua representação. Abra o shell do Django.

$ ./manage.py shell
>>> from core.serializers import PersonSerializer
>>> serializer = PersonSerializer()
>>> print(repr(serializer))
# PersonSerializer():
#     pk = IntegerField(label='ID', read_only=True)
#     first_name = CharField(max_length=30)
#     last_name = CharField(max_length=30)
#     email = EmailField(allow_blank=True, allow_null=True, max_length=254, required=False)
#     active = BooleanField(required=False)
#     created = DateTimeField(read_only=True)

É importante lembrar que as classes ModelSerializer não faz nenhuma mágica, são simplesmente um atalho para a criação das classes de serialização:

  • Os campos são definidos automaticamente.
  • Os métodos create() e update() são implementados por padrão de uma forma simplificada.

views.py: Criando views regulares usando nosso Serializer

Vamos criar uma view simples para visualizar os dados em json.

Edite o arquivo views.py

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from core.models import Person
from core.serializers import PersonSerializer

class JSONResponse(HttpResponse):
    """
    An HttpResponse that renders its content into JSON.
    """

    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

A raiz da nossa API será uma lista de todas as pessoas, ou podemos criar uma pessoa nova.

@csrf_exempt
def person_list(request):
    """
    List all persons, or create a new person.
    """
    if request.method == 'GET':
        persons = Person.objects.all()
        serializer = PersonSerializer(persons, many=True)
        return JSONResponse(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = PersonSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)

Note que nós queremos usar o POST mas não temos o CSRF Token, por isso usamos o @csrf_exempt.

Também vamos precisar visualizar os detalhes de cada pessoa. Assim podemos recuperar, atualizar ou deletar cada registro.

@csrf_exempt
def person_detail(request, pk):
    """
    Retrieve, update or delete a person.
    """
    try:
        person = Person.objects.get(pk=pk)
    except Person.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = PersonSerializer(person)
        return JSONResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = PersonSerializer(person, data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        person.delete()
        return HttpResponse(status=204)

Agora, vamos criar as urls. Crie um novo arquivo core/urls.py.

from django.conf.urls import url
from core import views

urlpatterns = [
    url(r'^persons/$', views.person_list),
    url(r'^persons/(?P<pk>[0-9]+)/$', views.person_detail),
]

E acrescente a seguinte linha em myproject/urls.py.

urlpatterns = [
    url(r'^', include('core.urls')),
    url(r'^admin/', include(admin.site.urls)),
]

Instalando httpie

Podemos usar o curl, mas o httpie é mais amigável, e escrito em Python.

$ sudo pip install httpie

Vamos usar o httpie digitando

$ http http://127.0.0.1:8000/persons/

HTTP/1.0 200 OK
Content-Type: application/json
Date: Sun, 15 Nov 2015 03:24:44 GMT
Server: WSGIServer/0.2 CPython/3.4.3
X-Frame-Options: SAMEORIGIN

[
    {
        "active": true, 
        "created": "2015-11-15T03:20:24.938378Z", 
        "email": "paul@email.com", 
        "first_name": "Paul", 
        "last_name": "Van Dyke", 
        "pk": 1
    }, 
    {
        "active": true, 
        "created": "2015-11-15T03:20:25.084990Z", 
        "email": "regis@email.com", 
        "first_name": "Regis", 
        "last_name": "Santos", 
        "pk": 2
    }
]

Veja os detalhes

$ http http://127.0.0.1:8000/persons/1/

Atenção: se você receber erro 301, muito provavelmente é porque você esqueceu da barra / no final da url.

Como seria em curl?

Assim

$ curl http://127.0.0.1:8000/persons/

[{"pk":1,"first_name":"Paul","last_name":"Van Dyke","email":"paul@email.com","active":true,"created":"2015-11-15T03:20:24.938378Z"},{"pk":2,"first_name":"Regis","last_name":"Santos","email":"regis@email.com","active":true,"created":"2015-11-15T03:20:25.084990Z"}]

GitHub: Se você quiser pode olhar meu GitHub, mas terá que ver os commits para ver os passos.

por Regis da Silva em 24 de April de 2016 às 21:00

Python Help

O estranho caso do else em loops

Uma das coisas que me chamou a atenção quando comecei a usar Python é o else. O seu uso “natural”, para definir um caminho alternativo para um if, não tem nada demais. O que é um pouco estranho é o fato de Python aceitar else em expressões de loop como for e while. Por exemplo, o código abaixo é perfeitamente válido:

# -*- coding:utf-8 -*-
import random
segredo = random.randint(0, 10)
for tentativa in range(1, 4):
    numero = input('Digite um número entre 0 e 9 (tentativa %d de 3):' % tentativa)
    if numero == segredo:
        print('você acertou!')
        break
else:
    print('você esgotou as suas tentativas')

Perceba que o else está alinhado com o for e não com o if. Neste caso, os comandos contidos nele somente serão executados se o loop não tiver sido encerrado por um break. O mesmo vale para o else em loops while. No exemplo abaixo, o else nunca será executado pois o loop é sempre encerrado por um break:

while True:
    break
else:
    print('nunca serei!')

Confesso que sempre tive uma certa dificuldade em lembrar o significado do else nesses casos, até porque raramente me deparo com eles. Até que certo dia assisti uma palestra em que o autor disse (não me recordo quem):

O else de loops em Python deveria ser chamado ‘nobreak’.

Pronto, nunca mais esqueci o significado do else em loops!:)


por Valdir Stumm Jr em 24 de April de 2016 às 20:38

April 23, 2016

PythonClub

Explicit is better than implicit

Esse post não é diretamente relacionado a desenvolvimento com Python, mas conta a história de uma das muitas experiências que passamos desenvolvendo e mostra como a filosofia e o mindset Python podem nos influenciar a tomar decisões melhores.

Contexto geral

Atualmente trabalho remotamente pela Toptal, uma empresa de consultoria em software com foco em trabalho remoto e que tem um processo seletivo bastante rígido para garantir uma qualidade acima da média para seus clientes (saiba mais sobre a Toptal aqui).

No time em que faço parte os papéis são bem definidos entre desenvolvedores front-end e back-end e faço parte da equipe de back-end, que usa principalmente Django nas aplicações. À medida que evoluímos e nos tornamos mais maduros como time, buscamos soluções que pudessem otimizar nosso processo de desenvolvimento.

Atualmente utilizamos CircleCI -- uma plataforma para integração e entrega contínuas -- para tarefas como rodar nossa suíte de testes, fazer a integração de nosso código, instanciar uma nova versão de nossos sistemas em um ambiente de staging e criar imagens Docker posteriormente colocadas em produção.

Melhorias

Nosso time constantemente reavalia processos, ferramentas e o resultado são discussões interessantes sobre como tornar nosso trabalho mais rápido e produtivo.

Recentemente começamos a utilizar um servidor NPM -- um dos mais usados gerenciadores de pacotes para Javascript -- privado para uma melhor separação de pacotes front-end, otimizando o tempo de build de assets de 47 para 25 segundos.

Na raiz do nosso projeto temos um package.json com o seguinte conteúdo:

{
  // [ ... ]
  "dependencies": {
    "cat": "^1.0.0",
    "front": "^1.0.0",
    "core": "^1.0.0",
  },
  // [ ... ]
}

Sendo que cat, front e core (renomeados para exemplificar) são pacotes mantidos por nós mesmos no NPM privado. Por padrão, se você lista o pacote com “^” (como por exemplo acima “^1.0.0”), o npm considera apenas o número que representa a major version, no caso o número 1, e fará o download da última versão que começa com 1.

Essa abordagem tem quatro pontos fracos:

  1. Ela pode quebrar seu código. Se pacote de terceiro atualizar, seu código pode não estar preparado para lidar com as novas funcionalidades adicionadas, principalmente porque libs evoluem tão rapidamente que se torna fácil acontecer uma atualização sem backwards compatibility.
  2. Você não sabe exatamente qual versão do pacote seu sistema está usando em produção. Para saber, você teria que acessar os servidores remotamente e executar o comando npm list, por exemplo (poderia fazer localmente também mas existe a possibilidade de que no momento em que ocorreu o deploy, aquele pacote estava em uma versão anterior à sua versão local).
  3. Você perde o controle de quando quer que seu sistema utilize a nova versão do pacote.
  4. Se você precisar fazer um rollback ou usar uma imagem antiga de seu sistema em produção, ainda assim ela vai utilizar a última versão do pacote, o que pode levar a mais dores de cabeça.

Problema

Recentemente tivemos um bug em produção, e uma mudança no pacote core resolveria. O que fazer com o sistema principal? Nada, não era necessária nenhuma alteração. Só precisaríamos gerar uma nova imagem Docker que ela seria montada do zero e no momento de instalar os pacotes npm, baixaria a última versão.

Bastava realizar rebuild na branch master no CircleCI, que assim que terminado ele trataria de enviar um webhook para o nossa ferramenta que cria imagens Docker. Nós utilizamos o seguinte padrão de nomenclatura dessas imagens:

myapp-production-<branch>-<sha[:7]>

Como não fizemos nenhuma alteração no sistema principal, o branch e o sha continuaram os mesmos.

Resumindo, nosso Docker recebeu um pedido de build para aquela branch e sha e, por padrão, primeiro procurou em seu cache de imagens se já existia alguma imagem pronta com aquele nome. O resultado foi que a mesma imagem, sem o hotfix, foi para produção (pois ela havia sido criada antes e no momento em que baixou os pacotes npm ainda não havia alterações no core).

Demoramos um pouco para perceber o problema, mas o suficiente para resolvê-lo sem que stakeholders percebessem.

Solução

Algum tempo depois discutimos e nós desenvolvedores back-end sugerimos a seguinte solução:

{
  // [ ... ]
  "dependencies": {
    "cat": "1.0.5",
    "front": "1.0.7",
    "core": "1.0.10",
  },
  // [ ... ]
}

Com essa abordagem:

  1. Você pode fazer rollback do seu código sem problemas pois o código antigo vai usar a versão antiga do pacote.
  2. Você tem controle sobre quando quer que seu sistema utilize a nova versão do pacote.
  3. Você sabe exatamente quais versões de pacotes seu sistema está utilizando, bastando abrir o packages.json.
  4. Caso uma nova versão quebre seu código, você pode voltar uma versão rapidamente até que o problema seja resolvido.

O problema que tivemos em produção não aconteceria caso tivéssemos utilizado a abordagem acima. Assim que os pacotes fossem atualizados, criaríamos uma pull request no repositório do sistema principal com as seguintes alterações:

diff --git i/package.json w/package.json
index eaae10d..5aa773b 100644
--- i/package.json
+++ w/package.json
@@ -9,7 +9,7 @@
   "dependencies": {
     "cat": "1.0.5",
     "front": "1.0.7",
-    "core": "1.0.10",
+    "core": "1.0.11",
   },

Após o merge, um novo build aconteceria no CircleCI, e um novo sha seria enviado via webhook. O Docker não encontraria nenhuma imagem com essa combinação de branch e sha e criaria uma nova do zero. Produção teria o hotfix e não haveria constrangimento.

Os desenvolvedores front-end não gostaram da ideia de ter que atualizar o arquivo toda vez que alguma dependência subisse de versão. Discutimos bastante e a última coisa que eu disse foi: “from the Zen of Python: explicit is better than implicit”.

Lição aprendida.

por Ivan Neto em 23 de April de 2016 às 02:00

April 22, 2016

Gustavo Niemeyer

Snappy Interfaces

As much anticipated, this week Ubuntu 16.04 LTS was released with integrated support for snaps on classic Ubuntu.

Snappy 2.0 is a modern software platform, that includes the ability to define rich interfaces between snaps that control their security and confinement, comprehensive observation and control of system changes, completion and undoing of partial system changes across restarts/reboots/crashes, macaroon-based authentication for local access and store access, preliminary development mode, a polished filesystem layout and CLI experience, modern sequencing of revisions, and so forth.

The previous post in this series described the reassuring details behind how snappy does system changes. This post will now cover Snappy interfaces, the mechanism that controls the confinement and integration of snaps with other snaps and with the system itself.

A snap interface gives one snap the ability to use resources provided by another snap, including the operating system snap (ubuntu-core is itself a snap!). That’s quite vague, and intentionally so. Software interacts with other software for many reasons and in diverse ways, and Snappy is a platform that has to mediate all of that according to user needs.

In practice, though, the mechanism is straightforward and pleasant to deal with. Without any snaps in the system, there are no interfaces available:

% sudo snap interfaces
error: no interfaces found

If we install the ubuntu-core snap alone (done implicitly when the first snap is installed), we can already see some interface slots being provided by it, but no plugs connected to them:

% sudo snap install ubuntu-core
75.88 MB / 75.88 MB [=====================] 100.00 % 355.56 KB/s 

% snap interfaces
Slot                 Plug
:firewall-control    -
:home                -
:locale-control      -
(...)
:opengl              -
:timeserver-control  -
:timezone-control    -
:unity7              -
:x11                 -

The syntax is <snap>:<slot> and <snap>:<plug>. The lack of a snap name is a shorthand notation for slots and plugs on the operating system snap.

Now let’s install an application:

% sudo snap install ubuntu-calculator-app
120.01 MB / 120.01 MB [=====================] 100.00 % 328.88 KB/s 

% snap interfaces
Slot                 Plug
:firewall-control    -
:home                -
:locale-control      -
(...)
:opengl              ubuntu-calculator-app
:timeserver-control  -
:timezone-control    -
:unity7              ubuntu-calculator-app
:x11                 -

At this point the application should work fine. But let’s instead see what happens if we take away one of these interfaces:

% sudo snap disconnect \
             ubuntu-calculator-app:unity7 ubuntu-core:unity7 

% /snap/bin/ubuntu-calculator-app.calculator
QXcbConnection: Could not connect to display :0

The application installed depends on unity7 to be able to display itself properly, which is itself based on X11. When we disconnected the interface that gave it permission to be accessing these resources, the application was unable to touch them.

The security minded will observe that X11 is not in fact a secure protocol. A number of system abuses are possible when we hand an application this permission. Other interfaces such as home would give the snap access to every non-hidden file in the user’s $HOME directory (those that do not start with a dot), which means a malicious application might steal personal information and send it over the network (assuming it also defines a network plug).

Some might be surprised that this is the case, but this is a misunderstanding about the role of snaps and Snappy as a software platform. When you install software from the Ubuntu archive, that’s a statement of trust in the Ubuntu and Debian developers. When you install Google’s Chrome or MongoDB binaries from their respective archives, that’s a statement of trust in those developers (these have root on your system!). Snappy is not eliminating the need for that trust, as once you give a piece of software access to your personal files, web camera, microphone, etc, you need to believe that it won’t be using those allowances maliciously.

The point of Snappy’s confinement in that picture is to enable a software ecosystem that can control exactly what is allowed and to whom in a clear and observable way, in addition to the same procedural care that we’ve all learned to appreciate in the Linux world, not instead of it. Preventing people from using all relevant resources in the system would simply force them to use that same software over less secure mechanisms instead of fixing the problem.

And what we have today is just the beginning. These interfaces will soon become much richer and more fine grained, including resource selection (e.g. which serial port?), and some of them will disappear completely in favor of more secure choices (Unity 8, for instance).

These are exciting times for Ubuntu and the software world.

@gniemeyer

por niemeyer em 22 de April de 2016 às 16:41

April 20, 2016

Gustavo Niemeyer

Snappy Changes

As announced last Saturday, Snappy Ubuntu Core 2.0 has just been tagged and made its way into the archives of Ubuntu 16.04, which is due for the final release in the next days. So this is a nice time to start covering interesting aspects of what is being made available in this release.

A good choice for the first post in this series is talking about how snappy performs changes in the system, as that knowledge will be useful in observing and understanding what is going on in your snappy platform. Let’s start with the first operation you will likely do when first interacting with the snappy platform — install:

% sudo snap install ubuntu-calculator-app
120.01 MB / 120.01 MB [================================================================] 100.00 % 1.45 MB/s

This operation is traditionally done on analogous systems in an ephemeral way. That is, the software has either a local or a remote database of options to install, and once the change is requested the platform of choice will start acting on it with all state for the modification kept in memory. If something doesn’t go so well, such as a reboot or even a crash, the modification is lost.. in the best case. Besides being completely lost, it might also be partially applied to the system, with some files spread through the filesystem, and perhaps some of the involved hooks run. After the restart, the partial state remains until some manual action is taken.

Snappy instead has an engine that tracks and controls such changes in a persistent manner. All the recent changes, pending or not, may be observed via the API and the command line:

% snap changes
ID   Status  ...  Summary
1    Done    ...  Install "ubuntu-calculator-app" snap

(the spawn and ready date/time columns have been hidden for space)

The output gives an overview of what happened recently in the system, whether pending or not. If one of these changes is unintendedly interrupted for whatever reason, the daemon will attempt to continue the requested change at the next opportunity.

Continuing is not always possible, though, because there are external factors that such a change will generally depend upon (the snap being available, the system state remaining similar, etc). In those cases, the change will fail, and any relevant modifications performed on the system while attempting to accomplish the defined goal will be undone.

Because such partial states are possible and need to be handled properly by the system, changes are in fact broken down into finer grained tasks which are also tracked and observable while in progress or after completion. Using the change ID obtained in the former command, we can get a better picture of what that changed involved:

% snap changes 1
Status ...  Summary
Done   ...  Download snap "ubuntu-core" from channel "stable"
Done   ...  Mount snap "ubuntu-core"
Done   ...  Copy snap "ubuntu-core" data
Done   ...  Setup snap "ubuntu-core" security profiles
Done   ...  Make snap "ubuntu-core" available
Done   ...  Download snap "ubuntu-calculator-app"
Done   ...  Mount snap "ubuntu-calculator-app"
Done   ...  Copy snap "ubuntu-calculator-app" data
Done   ...  Setup snap "ubuntu-calculator-app" security profiles
Done   ...  Make snap "ubuntu-calculator-app" available

(the spawn and ready date/time columns have been hidden for space)

Here we can observe an interesting implementation detail of the snappy integration into Ubuntu: the ubuntu-core snap is at the moment ~80MB, and contains the software bundled with the snappy platform itself. Instead of having it pre-installed, it’s only pulled in when the first snap is installed.

Another interesting implementation detail that surfaces here is the fact snaps are in fact mounted rather than copied into the system as traditional packaging systems do, and they’re mounted read-only. That means the operation of having the content of a snap in the filesystem is instantaneous and atomic, and so is removing it. There are no partial states for that specific aspect, and the content cannot be modified.

Coming back into the task list, we can see above that all the tasks that the change involved are ready and did succeed, as expected from the earlier output we had seen for the change itself. Being more of an introspection view, though, this tasks view will often also show logs and error messages for the individual tasks, whether in progress or not.

The following view presents a similar change but with an error due to an intentionally corrupted system state that snappy could not recover from (path got a busy mountpoint hacked in):

% sudo snap install xkcd-webserver
[\] Make snap "xkcd-webserver" available to the system
error: cannot perform the following tasks:
- Make snap "xkcd-webserver" available to the system (symlink 13 /snap/xkcd-webserver/current: file exists)

% sudo snap changes 2
Status  ...  Summary
Undone  ...  Download snap "xkcd-webserver" from channel "stable"
Undone  ...  Mount snap "xkcd-webserver"
Undone  ...  Copy snap "xkcd-webserver" data
Undone  ...  Setup snap "xkcd-webserver" security profiles
Error   ...  Make snap "xkcd-webserver" available to the system

.................................................................
Make snap "xkcd-webserver" available to the system

2016-04-20T14:14:30-03:00 ERROR symlink 13 /snap/xkcd-webserver/current: file exists

Note how reassuring that report looks. It says exactly what went wrong, at which stage of the process, and it also points out that all the prior tasks that previously succeeded had their modifications undone. The security profiles were removed, the mount point was taken down, and so on.

This sort of behavior is to be expected of modern operating systems, and is fundamental when considering systems that should work unattended. Whether in a single execution or across restarts and reboots, changes either succeed or they don’t, and the system remains consistent, reliable, observable, and secure.

In the next blog post we’ll see details about the interfaces feature in snappy, which controls aspects of confinement and integration between snaps.

@gniemeyer

por niemeyer em 20 de April de 2016 às 17:48

April 16, 2016

Gustavo Niemeyer

Snappy Ubuntu Core 2.0 is OUT

Ubuntu and Snappy community, it’s time to celebrate!

After another intense week and a long Saturday focused on observing and fine tuning the user experience, the development team is proud to announce that Snappy 2.0 has been tagged. As has been recently announced, this release of Snappy Ubuntu Core will be available inside Ubuntu proper, extending it with new capabilities in a seamless manner.

This is an important moment for the project, as it materializes most of the agreements that were made over the past year, and does so with the promise of stability. So you may trust that the important external APIs of the project (filesystem layout, snap format, REST API, etc) will not change from now on.

The features that went into this release are way too rich for me to describe in this post, but you may expect us to be covering the many interesting aspects of Snappy 2.0 in the coming weeks. Rich interfaces between snaps that control security and confinement, comprehensive observation and control of system changes, completion and undoing of partial system changes across restarts/reboots/crashes, macaroon-based authentication for local access and store access, preliminary development mode, a polished filesystem layout and CLI experience, modern sequencing of revisions, and so forth.

Still, the most remarkable aspect about this release to me is that it is a solid foundation. This release exports APIs and is constructed in a way to be proud of, and together with this team I will be delighted to spend the foreseeable future building a platform the world has never seen.

As a final note, I can’t thank the development team enough for the dedication they have put into the project over the past year, and specially over these last two weeks. You were the make it or break it of this project, and you made it.

Thank you!

@gniemeyer

por niemeyer em 16 de April de 2016 às 20:56

April 14, 2016

Filipe Saraiva

AAAI e o apoio ao acesso aberto na Inteligência Artificial

AAAI é a sigla para Association for the Advancement of Artificial Intelligence, sociedade científica voltada para congregar os pesquisadores das diferentes vertentes do que comumente chamamos inteligência artificial: inteligência computacional, métodos metaheurísticos, agentes e sistemas multiagentes, raciocínio lógico automatizado, IA forte, aplicações de IA, entre muitas outras linhas.

Apesar de pequena quando comparada a esses grandes “guarda-chuvas de sociedades científicas” como o são o IEEE e a ACM, a AAAI é uma sociedade de renome e respeito na comunidade. Fundada em 1979, teve como presidentes importantes nomes da área como Marvin Minsky e John McCarthy, além de contar com influentes pesquisadores na sua lista de filiados.

Algo que sempre admirei na AAAI é o apoio ao acesso aberto nas suas publicações. Todas as conferências organizadas pela sociedade (como a AAAI – Conference on Artificial Intelligence ou a IAAI – Innovative Applications of Artificial Intelligence) e mesmo aquelas apenas apoiadas (como a IJCAI –  International Joint Conference on Artificial Intelligence) tem seus proceedings disponibilizados na internet, livre de taxas e acessível a qualquer pessoa com uma conexão.

A AAAI não tem uma revista científica própria, mas apoia a JAIR – Journal of Artificial Intelligence Research, também de acesso aberto. Por outro lado a sociedade mantém uma revista que publica artigos científicos e textos relacionados com a associação, a AI Magazine, que segue o modelo delayed open access – cada número é disponibilizado imediatamente aos sócios e torna-se de acesso aberto 15 meses após a publicação.

Quem conhece as publicações listadas sabe que são de excelente qualidade, todas listadas como A1 ou A2 na classificação de periódicos e conferências da área de ciência da computação do Qualis CAPES.

Há um endereço para a biblioteca digital da AAAI que concentra links para os diferentes tipos de publicações, mas você pode preferir ir direto para o endereço das conferências, para o da AI Magazine ou do JAIR.

Sabemos que todo esse esforço para produção e disponibilização gratuita desses materiais tem seus custos, e uma forma de apoiar a continuação desse trabalho e política é se associando à AAAI. Em 2016 a sociedade lançou uma novidade muito bem-vinda para nós brasileiros e demais moradores de países em desenvolvimento: há uma taxa especial de associação, que reduz o valor de uma filiação regular de U$ 145,00 para U$ 20,00, e da filiação estudantil de U$ 75,00 para U$ 18,00. Saiba mais sobre essa e outras informações sobre como se associar na página AAAI Membership.

Para mim, as sociedades científicas tem papel fundamental nas políticas de acesso aberto. Faz sentido que as editoras científicas lutem contra essa forma de disponibilização de conteúdo, afinal elas são empresas e tentam de todas as formas manter o modelo de negócios que as deu tanto lucro, por mais que a tecnologia tenha mudado e os custos que elas tem hoje seja absurdamente inferior ao que era anos atrás. Já as sociedades científicas normalmente são grupos que não visam lucro, e além disso são as “fornecedoras” da principal mão de obra do modelo de publicação científica – os cientistas que escrevem, revisam, editam e gerenciam artigos são membros de diferentes sociedades de suas áreas. Esses grupos, portanto, podem criar políticas de acesso aberto para suas produções, sem ônus ao seus objetivos de congregar pesquisadores, qualificar e distribuir o conhecimento produzido por eles, e assim avançar a ciência produzida naquela área.

Me filiei à AAAI e espero apoiar e participar dessa sociedade por muitos anos. Torço para um dia termos mais sociedades científicas como esta.

por Filipe Saraiva em 14 de April de 2016 às 15:06

April 12, 2016

Aprenda Python

Faça programas simples

Vá por mim. Faça programas tão simples que: - Qualquer programador entenda. - Você se lembre do propósito dele quando lê-lo alguns meses (ou anos) mais tarde. Tenha clareza do objetivo a ser alcançado. Construa um sistema, ao invés de "um apanhado de programas". Conheça a forma de comunicação entre as diversas partes do sistema. Dê nomes que façam sentido, dentro de um contexto.

por Vinicius Assef (noreply@blogger.com) em 12 de April de 2016 às 13:01

April 10, 2016

Aprenda Python

Eu devo usar Python 2 ou Python 3?

Afinal, qual Python usar? Se você vai começar a estudar Python agora ou iniciar um projeto novo, escolha Python 3 e leia Por onde começar com Python? Nesse artigo vou citar alguns motivos para não usar Python 2, quais são as diferenças entre o Python 2 o Python 3 e como saber se as ferramentas que você pretende usar são compatíveis com Python 3. Python 2 já vem sendo chamado de "Python

por Vinicius Assef (noreply@blogger.com) em 10 de April de 2016 às 03:38

April 08, 2016

Aprenda Python

Pedir permissão ou pedir perdão?

Em Python costumamos ouvir que é [mais fácil pedir perdão do que pedir permissão (It's easier to ask for forgiveness than permission)](https://docs.python.org/3/glossary.html#term-eafp), mas o que isso realmente significa? O que vemos normalmente, conhecida como programação defensiva, é: import os.path if os.path.exists('meu_arquivo.txt'): with open('meu_arquivo.txt') as f:

por Vinicius Assef (noreply@blogger.com) em 08 de April de 2016 às 04:27

April 07, 2016

Aprenda Python

Por que desenvolver um sistema usando Baby Steps

O maior problema da maioria de nós, programadores, não é tecnologia. Nosso maior problema é método de trabalho. A maioria de nós, programadores, aprende sozinho, domina várias linguagens, frameworks e bibliotecas mas colhe os maus frutos de processos e métodos de trabalho que poderiam ser melhores. Muito melhores. Há alguns dias eu li a seguinte pergunta: > > > Eu desenvolvi um

por Vinicius Assef (noreply@blogger.com) em 07 de April de 2016 às 11:34

April 06, 2016

Aprenda Python

Non-technical leaders and IT projects

Why many non-technical project leaders fail? What could they do to change the game? Every software project should have a tech leader ------------------------------------------------ It sounds obvious and apparently happens everytime & everywhere, but it doesn't. Bad things happen when a non-technical person (or a "boss") has a seat on the technical leader's chair: - Be prepared for

por Vinicius Assef (noreply@blogger.com) em 06 de April de 2016 às 16:17

April 01, 2016

Programando Ciência

[EVENT] SciPy 2016

Hey scientist!
In July we will have one important event for the world community of scientific Python: the SciPy 2016! Do you want to know what will happen there, and how to be a part of it?

The 15th edition of the Scientific Computing with Python conference, SciPy 2016, will happen between July 11 and 17, on Austin, Texas, USA. It will consist of two days of tutorials, three days of talks, and two days of developer sprints.

At the conference, these topics will be approached:

  • Scientific Computing in Python;
  • Python in Data Science;
  • Earth and Space Science;
  • Engineering;
  • Medicine and Biology;
  • Social Sciences;
  • Special Purpose Databases;
  • Case Studies in Industry and Education;
  • Reproducibility.

Besides us, there are several nice communities and enterprises supporting the event:

The keynote speakers are also defined:

  • Andreas Klöckner, assistant professor from the Illinois University at Urbana-Champaign.
  • Hanna Wallach, researcher from Microsoft Research in New York, and professor from the Massachusetts University at Amherst.

The deadline for talk and poster proposals ends today, April 1st! Go for it and present your research! If you don’t want to present but want to help the event, you can also be a volunteer. Give them a help:)

That’s it scientist! Show up, learn more about Python and present your ideas to some of the greatest programmers of the Earth!
Gigaregards! See you next time!


Did you like this post? Please comment and share with your friends!
Want to download Programando Ciência codes? Go to our GitHub!
Make a donation for Programando Ciência!
Like us also on Facebook: www.facebook.com/programandociencia
I’m on Twitter! Follow me if you can! @alexdesiqueira


por alexandrejaguar em 01 de April de 2016 às 14:14

[EVENTO] SciPy 2016

Faaaaaaala cientista!
Em julho, teremos mais um evento importante para a comunidade mundial de Python científico: a SciPy 2016! Quer saber o que vai acontecer lá, e como participar?

A décima quinta edição da conferência de computação científica com Python, a SciPy 2016, acontecerá entre 11 e 17 de julho na cidade de Austin, no Texas, EUA. Serão dois dias de tutoriais, três dias de palestras e dois dias de desenvolvimento de software (os famosos sprints).

Os seguintes tópicos serão abordados na conferência:

  • Computação científica com Python;
  • Python em ciência de dados;
  • Ciências espaciais e da Terra;
  • Engenharia;
  • Medicina e biologia;
  • Ciências sociais;
  • Bancos de dados especiais;
  • Estudos de caso na indústria e educação;
  • Reproducibilidade.

Além de nós, há várias comunidades e empresas bacanas apoiando o evento:

Também já estão definidos os palestrantes principais:

  • Andreas Klöckner, professor assistente da Universidade de Illinois em Urbana-Champaign.
  • Hanna Wallach, pesquisadora na Microsoft Research em Nova Iorque, e professora na Universidade de Massachusetts em Amherst.

A submissão de palestras e pôsteres acaba hoje, primeiro de abril! Corre lá para apresentar sua pesquisa! Se não quiser se apresentar, também estão abertas as inscrições para voluntários. Dê uma forcinha:)

É isso aí cientista! Compareça, aprenda mais sobre Python e exponha suas ideias a alguns dos maiores programadores do planeta!
Um giga abraço! Até a próxima!


Gostou? Curta e compartilhe com seus amigos!
Quer baixar os códigos do Programando Ciência? Corre lá no nosso GitHub!
Faça uma doação pro Programando Ciência!
Curta a gente também no Facebook: www.facebook.com/programandociencia
Estou no Twitter! Siga-me se puder! @alexdesiqueira


por alexandrejaguar em 01 de April de 2016 às 13:35

Magnun Leno

Hack ‘n’ Cast v0.19 - Introdução à Eletrônica Digital

Uma breve introdução à eletrônica digital, passando pela sua antecessora até chegar aos flip-flops.

Baixe o episódio e leia o shownotes

por Magnun em 01 de April de 2016 às 03:01

March 31, 2016

Aprenda Python

Qual a forma mais pythônica de chamar uma função com parâmetros?

Outro dia surgiu uma pergunta interessante na lista Python Brasil. Ela era mais ou menos assim: > Dada uma função declarada assim: > > def func1(foo=None, bar=None): > > Qual é a forma mais pythônica de chamá-la? > > Assim: func1('abc', 'def') > > ou assim: func1(foo='abc', bar='def') > ? Eu achei a dúvida interessante e resolvi escrever a respeito. **Resposta versão TL;DR:** use o

por Vinicius Assef (noreply@blogger.com) em 31 de March de 2016 às 11:21

March 26, 2016

Filipe Saraiva

Workshop de Software Livre 2016 – chamada de artigos e ferramentas

wsl2016-banner

Está aberta a chamada de trabalhos do Workshop de Software Livre (WSL), o evento acadêmico irmão do Fórum Internacional de Software Livre (FISL). Esse ano temos uma interessante novidade: além da já usual chamada de artigos, há também uma chamada específica para ferramentas livres.

O WSL publica artigos científicos com os mais variados temas de interesse das comunidades de software livre, como dinâmica social, gerenciamento, processos de desenvolvimento e motivações de contribuidores em comunidades, adoção e estudos de caso sobre software livre, aspectos jurídicos e econômicos, estudos sociais e históricos sobre o tema, e muito mais.

Esse ano, com a chamada de ferramentas, iremos publicar artigos que descrevem software, garantindo que o programa descrito atende a diversos critérios de qualidade e de adequação quanto a ser software livre.

Todos os artigos serão publicados no repositório de acesso aberto do WSL. Esse ano estamos trabalhando forte para termos um ISSN e DOI para as publicações.

O deadline é dia 10 de abril. Os artigos podem ser submetidos em português, inglês ou espanhol.

Saiba mais na página oficial do WSL.

por Filipe Saraiva em 26 de March de 2016 às 16:55

March 25, 2016

Aprenda Python

Nomenclatura para nomes em Bancos de Dados

Vou descrever a forma como eu dou nomes a objetos de Bancos de Dados. Essas regras funcionam para mim, mas não significa que vão funcionar para você. Introdução ---------- Eu não: - costumo usar prefixos nem sufixos; - costumo identificar tipo de dado nos nomes; Eu uso: - todos os nomes em minúsculas; - o caractere sublinhado como separador entre as palavras. Lembre-se: toda regra tem

por Vinicius Assef (noreply@blogger.com) em 25 de March de 2016 às 00:56

March 20, 2016

Programando Ciência

[EVENT] SciPy Latin America 2016

Hey scientist!
Have you ever thought about participating in the largest Latin America event about scientific Python, and connecting to the members of the coolest programming community of the world?

That’s right: this year will be easier for us Brazilians to go to SciPyLA! The event will be hosted in Florianópolis, the island of magic!

Registration will open soon, and the event will happen in May, 16-20. Do you want to present some stuff? Tutorials and talks can be submitted until April, 8. Go for it dude! Show how you use Python on your research, or present a tutorial about that tool you use:)

Besides us, there are several nice communities supporting the event:

Enjoy Floripa and know also the beaches, the city main market, the Conceição pond, among several other touristic points! Also, meet new contacts and have a beer (or a tea) with our organization friends, such as Ivan Ogawasara, Raniere Silva, and all the others.

That’s it scientist! Enjoy the opportunity to integrate yourself with many people and learn about the nicest scientific subjects, all of that using Python!
Gigaregards! See you next time!


Did you like this post? Please comment and share with your friends!
Want to download Programando Ciência codes? Go to our GitHub!
Make a donation for Programando Ciência!
Like us also on Facebook: www.facebook.com/programandociencia
I’m on Twitter! Follow me if you can! @alexdesiqueira


por alexandrejaguar em 20 de March de 2016 às 06:47

[EVENTO] SciPy Latin America 2016

Faaaaaaala cientista!
Já pensou em estar no maior encontro sobre Python científico da América Latina e aprender várias coisas novas em tutoriais e palestras, além de se conectar com os membros da comunidade de programação mais bacana do planeta?

É isso aí: esse ano vai ser mais fácil para nós brazucas comparecermos na SciPyLA! O evento acontecerá em Florianópolis, a ilha da magia!

As inscrições abrirão em breve, e a SciPyLA acontecerá entre 16 e 20 de maio. Quer se apresentar? A submissão de palestras e tutoriais está aberta até 08 de abril. Vai que é sua, marajá! Mostre como você usa Python na sua pesquisa, ou ministre um tutorial sobre aquela ferramenta que você usa:)

Além de nós, há várias comunidades e empresas bacanas apoiando a SciPyLA:

Aproveite Floripa e conheça também as praias, o mercado municipal, a lagoa da Conceição, entre diversos pontos turísticos! Também faça novos contatos e tome uma cerveja (ou um chá) com os amigos da organização, como o Ivan Ogawasara, o Raniere Silva, entre muitos outros.

É isso aí cientista! Aproveite a oportunidade de se integrar com várias pessoas e aprender sobre os mais variados assuntos científicos, utilizando Python!
Um giga abraço! Até a próxima!


Gostou? Curta e compartilhe com seus amigos!
Quer baixar os códigos do Programando Ciência? Corre lá no nosso GitHub!
Faça uma doação pro Programando Ciência!
Curta a gente também no Facebook: www.facebook.com/programandociencia
Estou no Twitter! Siga-me se puder! @alexdesiqueira


por alexandrejaguar em 20 de March de 2016 às 06:40

March 17, 2016

Aprenda Python

Uma visão sobre frameworks fullstack

Antes de começar ---------------- Esse artigo faz parte de uma série que estou produzindo sobre frameworks web em Python. Em breve escreverei outros. Leia [Qual é a diferença entre framework fullstack e microframework?](http://aprenda-python.blogspot.com/2016/02/qual-a-diferenca-entre-framework-fullstack-e-micro.html) antes de ler esse artigo, para entender o contexto. Introdução ----------

por Vinicius Assef (noreply@blogger.com) em 17 de March de 2016 às 06:22

Qual a diferença entre framework fullstack e microframework?

**Importante:** o termo "fullstack", quando referindo-se a frameworks web em Python, não inclui, necessariamente, tecnologias de frontend. Conceitos --------- Os frameworks fullstack vêm com todas as ferramentas (ou a maioria delas) necessárias para desenvolver o backend de uma aplicação Web, em Python: - Tratamento de requisição (request) HTTP; - Envio da resposta (response) a uma

por Vinicius Assef (noreply@blogger.com) em 17 de March de 2016 às 06:21

March 14, 2016

PythonClub

TDD com Python e Flask

Baseado na palestra que ofereci no encontro do Grupy-SP, em 12 de março de 2016. O código dessa atividade está disponível no meu GitHub.

A ideia desse exercício é introduzir a ideia de test driven development (TDD) usando Python e Flask — digo isso pois a aplicação final desse “tutorial” não é nada avançada, tampouco funcional. E isso se explica por dois motivos: primeiro, o foco é sentir o que é o driven do TDD, ou seja, como uma estrutura de tests first (sempre começar escrevendo os testes, e não a aplicação) pode guiar o processo de desenvolvimento; e, segundo, ser uma atividade rápida, de mais ou menos 1h.

Em outras palavras, não espere aprender muito de Python ou Flask. Aqui se concentre em sentir a diferença de utilizar um método de programar. Todo o resto é secundário.

1. Preparando o ambiente

Requisitos

Para esse exercício usaremos o Python versão 3.5.1 com o framework Flask versão 0.10.1. É recomendado, mas não necessário, usar um virtualenv.

Como o código é bem simples, não acho que você vá ter muitos problemas se utilizar uma versão mais antiga do Python (ou mesmo do Flask). Em todo caso, em um detalhe ou outro você pode se deparar com mensagens distintas se utilizar o Python 2.

Você pode verificar a versão do seu Python com esse comando:

$ python --version                                                                                    

Dependendo da sua instalação, pode ser que você tenha que usar python3 ao invés de python — ou seja, o comando todo deve ser python3 --version. O resultado deve ser esse:

Python 3.5.1

E instalar o Flask assim:

$ pip install Flask

O pip é um gerenciador de pacotes do Python. Ele vem instalado por padrão nas versões mais novas do Python. Dependendo da sua instalação, pode ser que você tenha que usar pip3 ao invés de pip — ou seja, o comando todo deve ser pip3 install Flask. Com esse comando ele vai instalar o Flask e qualquer dependência que o Flask tenha:

Collecting Flask
Collecting Jinja2>=2.4 (from Flask)
  Using cached Jinja2-2.8-py2.py3-none-any.whl
Collecting itsdangerous>=0.21 (from Flask)
Collecting Werkzeug>=0.7 (from Flask)
  Using cached Werkzeug-0.11.4-py2.py3-none-any.whl
Collecting MarkupSafe (from Jinja2>=2.4->Flask)
Installing collected packages: MarkupSafe, Jinja2, itsdangerous, Werkzeug, Flask
Successfully installed Flask-0.10.1 Jinja2-2.8 MarkupSafe-0.23 Werkzeug-0.11.4 itsdangerous-0.24

Arquivos

Vamos usar, nesse exercício, basicamente 2 arquivos:

  • app.py: onde criamos nossa aplicação web;
  • tests.py: onde escrevemos os testes que guiarão o desenvolvimento da aplicação, e que, também, garantirão que ela funcione.

2. Criando a base dos testes

No arquivo tests.py vamos usar o módulo unittest, que já vem instalado por padrão no Python.

Criaremos uma estrutura básica para que, toda vez que esse arquivo seja executado, o unittest se encarregue de encontrar todos os nossos testes e rodá-los.

Vamos começar escrevendo com um exemplo fictício: testes para um método que ainda não criamos, um método que calcule números fatoriais. A ideia é só entender como escreveremos testes em um arquivo (tests.py) para testar o que escreveremos no outro arquivo (app.py).

A estrutura básica a seguir cria um caso de teste da unittest e, quando executada, teste nosso método fatorial(numero) para todos os números de 0 até 6:

import unittest


class TestFatorial(unittest.TestCase):

    def test_fatorial(self):
        self.assertEqual(fatorial(0), 1)
        self.assertEqual(fatorial(1), 1)
        self.assertEqual(fatorial(2), 2)
        self.assertEqual(fatorial(3), 6)
        self.assertEqual(fatorial(4), 24)
        self.assertEqual(fatorial(5), 120)
        self.assertEqual(fatorial(6), 720)

if __name__ == '__main__':
    unittest.main()

Se você conhece um pouco de inglês, pode ler o código em voz alta, ele é quase auto explicativo: importamos o módulo unittest (linha 1), criamos um objeto que é um caso de teste do método fatorial (linha 4), escrevemos um método de teste (linha 6) e esse método se assegura de que o retorno de fatorial(numero) é o resultado que esperamos (linhas 5 a 11).

Agora podemos rodar os testes assim:

$ python testes.py

Veremos uma mensagem de erro, NameError, pois não definimos nossa função fatorial(numero):

E
======================================================================
ERROR: test_fatorial (__main__.TestSimples)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 7, in test_fatorial
    self.assertEqual(fatorial(0), 1)
NameError: name 'fatorial' is not defined

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

Tudo bem, a ideia não é brincar com matemática agora. Mas vamos criar essa função lá no app.py só para ver como a gente pode “integrar” esses dois arquivos — ou seja, fazer o tests.py testar o que está em app.py.

Vamos adicionar essas linhas ao app.py:

def fatorial(numero):
    if numero in (0, 1):
        return 1
    return numero * fatorial(numero - 1)

E adicionar essa linha no topo do tests.py:

from app import fatorial

Agora, rodando os testes vemos que a integração entre app.py e tests.py está funcionando:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Ótimo. Chega de matemática, vamos ao TDD com Flask, um caso muito mais tangível do que encontramos no nosso dia-a-dia.

3. Primeiros passos para a aplicação web

Criando um servidor web

Como nosso foco é começar uma aplicação web, podemos descartar os testes e o método fatorial que criamos no passo anterior. Ao invés disso, vamos escrever um teste simples, para ver se conseguimos fazer o Flask criar um servidor web.

Descarte tudo do tests.py substituindo o conteúdo do arquivo por essas linhas:

import unittest
from app import meu_web_app


class TestHome(unittest.TestCase):

    def test_get(self):
        app = meu_web_app.test_client()
        response = app.get('/')
        self.assertEqual(200, response.status_code)

if __name__ == '__main__':
    unittest.main()

Esse arquivo agora faz quatro coisas referentes a nossa aplicação web:

  1. Importa o objeto meu_web_app (que ainda não criamos) do nosso arquivo app.py;
  2. Cria uma instância da nossa aplicação web específica para nossos testes (é o método meu_web_app.test_client(), cujo retorno batizamos de app);
  3. Tenta acessar a “raíz” da nossa aplicação — ou seja, se essa aplicação web estivesse no servidor pythonclub.com.br estaríamos acessando http://pythonclub.com.br/.
  4. Verifica se, ao acessar esse endereço, ou seja, se ao fazer a requisição HTTP para essa URL, temos como resposta o código 200, que representa sucesso.

Os códigos de status de requisição HTTP mais comuns são o 200 (sucesso), 404 (página não encontrada) e 302 (redirecionamento) — mas a lista completa é muito maior que isso.

De qualquer forma não conseguiremos rodar esses testes. O interpretador do Python vai nos retornar um erro:

ImportError: cannot import name 'meu_web_app'

Então vamos criar o objeto meu_web_app lá no app.py. Descartamos tudo que tínhamos lá substituindo o contéudo do arquivo por essas linhas:

from flask import Flask

meu_web_app = Flask()

Apenas estamos importando a classe principal do Flask, e criando uma instância dela. Em outras palavras, estamos começando a utilizar o framework.

E agora o erro muda:

Traceback (most recent call last):
  File "tests.py", line 2, in <module>
    from app import meu_web_app
  File "/Users/cuducos/Desktop/flask/app.py", line 3, in <module>
    meu_web_app = Flask()
TypeError: __init__() missing 1 required positional argument: 'import_name'

Importamos nosso meu_web_app, mas quando instanciamos o Flask temos um problema. Qual problema? O erro nos diz: quando tentamos chamar Flask() na linha 3 do app.py está faltando um argumento posicional obrigatório (missing 1 required positional argument). Estamos chamando Flask() sem nenhum argumento. O erro ainda nos diz que o que falta é um nome (import_name). Vamos batizar nossa instância com um nome:

meu_web_app = Flask(`meu_web_app`)

E agora temos uma nova mensagem de erro, ou seja, progresso!

Eu amo testes que falham! A melhor coisa é uma notificação em vermelho me dizendo que os testes estão falhando. Isso significa que eu tenho testes e que eles estão funcionando!

Bruno Rocha

F
======================================================================
FAIL: test_get (__main__.TestHome)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 10, in test_get
    self.assertEqual(200, response.status_code)
AssertionError: 200 != 404

----------------------------------------------------------------------
Ran 1 test in 0.015s

FAILED (failures=1)

Temos uma aplicação web rodando, mas quando tentamos acessar a raíz dela, ela nos diz que a página não está definida, não foi encontrada (é o que nos diz o código 404).

Criando nossa primeira página

O Flask facilita muito a criação de aplicações web. De forma simplificada a qualquer método Python pode ser atribuída uma URL. Isso é feito com um decorador:

@app.route('/')
def pagina_inicial():
    return ''

Adicionando essas linhas no app.py, os testes passam:

.
----------------------------------------------------------------------
Ran 1 test in 0.013s

OK

Se a curiosidade for grande, esse artigo (em inglês) explica direitinho como o Flask.route(rule, **options) funciona: Things which aren't magic - Flask and @app.route.

Para garantir que tudo está certinho mesmo, podemos adicionar mais um teste. Queremos que a resposta do servidor seja um HTML:

def test_content_type(self):
    app = meu_web_app.test_client()
    response = app.get('/')
    self.assertIn('text/html', response.content_type)

Rodando os testes, veremos que agora temos dois testes. E ambos passam!

Eliminando repetições

Repararam que duas linhas se repetem nos métodos test_get() e test_content_type()?

app = meu_web_app.test_client()
response = app.get('/')

Podemos usar um método especial da classe unittest.TestCase para reaproveitar essas linhas. O método TestCase.setUp() é executado ao iniciar cada teste, e através do self podemos acessar objetos de um método a partir de outro método:

class TestHome(unittest.TestCase):

    def setUp(self):
        app = meu_web_app.test_client()
        self.response = app.get('/')

    def test_get(self):
        self.assertEqual(200, self.response.status_code)

    def test_content_type(self):
        self.assertIn('text/html', self.response.content_type)

Não vamos precisar nesse exemplo, mas o método TestCase.tearDown() é executado ao fim de cada teste (e não no início, como a setUp()). Ou seja, se precisar repetir algum comando sempre após cada teste, a unittest também faz isso para você.

4. Preenchendo a página

Conteúdo como resposta

Temos um servidor web funcionando, mas não vemos nada na nossa aplicação web. Podemos verificar isso em três passos rápidos:

Primeiro adicionamos essas linhas ao app.py para que, quando executarmos o app.py (mas não quando ele for importado no tests.py), a aplicação web seja iniciada:

if __name__ == "__main__":
    meu_web_app.run()

Depois executamos o arquivo:

$ python app.py

Assim vemos no terminal essa mensagem:

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Se acessarmos essa URL no nosso navegador, podemos ver a aplicação rodando: http://127.0.0.1:5000/.

E veremos que realmente não há nada, é uma página em branco.

Vamos mudar isso! Vamos construir o que seria uma página individual, mostrando quem a gente é. Na minha vou querer que esteja escrito (ao menos), meu nome. Então vamos escrever um teste para isso:

def test_content(self):
    self.assertIn('Eduardo Cuducos', self.response.data)

Feito isso, teremos uma nova mensagem de erro nos testes:

TypeError: a bytes-like object is required, not 'str'

Essa mensagem nos diz que estamos comparando uma string com um objeto que é de outro tipo, que é representado por bytes. Não é isso que queremos. Como explicitamente passamos para o teste uma string com nosso nome, podemos assumir que é o self.response.data que vem codificado em bytes. Vamos decodificá-lo para string.

Bytes precisam ser decodificados para string (método decode). Strings precisam ser codificados para bytes para então mandarmos o conteúdo para o disco, para a rede (método encode).

Henrique Bastos

def test_content(self):
    self.assertIn('Eduardo Cuducos', self.response.data.decode('utf-8'))

Assim temos uma nova mensagem de erro:

AssertionError: 'Eduardo Cuducos' not found in "b''"

Nossa página está vazia, logo o teste não consegue encontrar meu nome na página. Vamos resolver isso lá no app.py:

@meu_web_app.route('/')
def pagina_inicial():
    return 'Eduardo Cuducos'

Agora temos os testes passando, e podemos verificar isso vendo que temos o nome na tela do navegador.

...
----------------------------------------------------------------------
Ran 3 tests in 0.015s

OK

Apresentando o conteúdo com HTML

O Python e o Flask cuidam principalmente do back-end da apliacação web — o que ocorre “por trás dos panos” no lado do servidor.

Mas temos também o front-end, que é o que o usuário vê, a interface com a qual o usuário interage. Normalmente o front-end é papel de outras linguagens, como o HTML, o CSS e o JavaScript.

Vamos começar com um HTML básico, criando a pasta templates e dentro dela o arquivo home.html:

<!DOCTYPE HTML>
<html>
  <head>
    <title>Eduardo Cuducos</title>
  </head>
  <body>
    <h1>Eduardo Cuducos</h1>
    <p>Sociólogo, geek, cozinheiro e fã de esportes.</p>
  </body>
</html>

Se a gente abrir essa página no navegador já podemos ver que ela é um pouco menos do que o que a gente tinha antes. Então vamos alterar nosso test_content() para garantir que ao invés de termos somente a string com nosso nome na aplicação, tempos esse template renderizado:

def test_content(self):
    response_str = self.response.data.decode('utf-8')
    self.assertIn('<title>Eduardo Cuducos</title>', str(response_str))
    self.assertIn('<h1>Eduardo Cuducos</h1>', str(response_str))
    self.assertIn('<p>Sociólogo, ', str(response_str))

Assim vemos nossos testes falharem:

F..
======================================================================
FAIL: test_content (__main__.TestHome)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 18, in test_content
    self.assertIn('<title>Eduardo Cuducos</title>', str(self.response.data))
AssertionError: '<title>Eduardo Cuducos</title>' not found in "b'Eduardo Cuducos'"

----------------------------------------------------------------------
Ran 3 tests in 0.017s

FAILED (failures=1)

Criamos um HTML, mas ainda não estamos pedindo para o Flask utilizá-lo. Temos nossa home.html dentro da pasta templates pois é justamente lá que o Flask vai buscar templates. Sabendo disso, podemos fazer nosso método index() retornar não a string, mas o template:

from flask import Flask, render_template



@meu_web_app.route('/')
def pagina_inicial():
    return render_template('home.html')

Assim voltamos a ter testes passando — e a página fica um pouco mais apresentável.

Formatando o conteúdo com CSS

Para não perder muito o foco do Python, TDD e Flask, vamos utilizar um framework CSS que se chama Bootstrap. Incluindo o CSS desse framework no nosso HTML, e utilizando algumas classes especificas dele, conseguimos dar uma cara nova para nossa aplicação.

Vamos escrever um teste para verificar se estamos mesmo carregando o Bootstrap:

def test_bootstrap_css(self):
    response_str = self.response.data.decode('utf-8')
    self.assertIn('bootstrap.min.css', response_str)

Os testes falham. Temos que linkar o CSS do Bootstrap em nosso HTML. Ao invés de baixar o Bootstrap, vamos utilizar o servidor CDN que eles mesmo recomendam. É só incluir essa linha no <head> do nosso HTML:

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

Agora, com os testes passando, vamos utilizar as classes do Bootstrap para formatar melhor nossa página. Vamos retirar nosso <h1> e <p> e, ao invés disso, partir do componente Jumbotron fazendo algumas pequenas alterações:

<div class="container">
  <div class="jumbotron">
    <img src="https://avatars.githubusercontent.com/u/4732915?v=3&s=128" alt="Eduardo Cuducos" class="img-circle">
    <h1>Eduardo Cuducos</h1>
    <p>Sociólogo, geek, cozinheiro e fã de esportes.</p>
    <p><a class="btn btn-primary btn-lg" href="http://twitter.com/cuducos" role="button">Me siga no Twitter</a></p>
  </div>
</div>

Com essa página “incrementada” podemos ainda refinar nossos testes, garantindo que sempre temos a foto e o link:

def test_profile_image(self):
    response_str = self.response.data.decode('utf-8')
    self.assertIn('<img src="', response_str)
    self.assertIn('class="img-circle"', response_str)

def test_link(self):
    response_str = self.response.data.decode('utf-8')
    self.assertIn('href="http://twitter.com/cuducos"', response_str)
    self.assertIn('>Me siga no Twitter</a>', response_str)

Pronto, agora temos uma página formatada para mostrar para nossos colegas, com todos os testes passando:

......
----------------------------------------------------------------------
Ran 6 tests in 0.024s

OK

5. Conteúdos dinâmicos

Passando variáveis para o contexto do template

O problema da nossa página é que ela é estática. Vamos usar o Python e o Flask para que quando a gente acesse /cuducos a gente veja a minha página, com meus dados. Mas caso a gente acesse /z4r4tu5tr4, a gente veja o conteúdo referente ao outro Eduardo que palestrou comigo no Grupy.

Antes de mudar nossas URLS, vamos refatorar nossa aplicação e — importantíssimo! — os testes tem que continuar passando. A ideia é evitar que o conteúdo esteja “fixo” no template. Vamos fazer o conteúdo ser passado do método pagina_principal() para o template.

A ideia é extrair todo o conteúdo do nosso HTML criando um dicionário no app.py:

CUDUCOS = {'nome': 'Eduardo Cuducos',
           'descricao': 'Sociólogo, geek, cozinheiro e fã de esportes.',
           'url': 'http://twitter.com/cuducos',
           'nome_url': 'Twitter',
           'foto': 'https://avatars.githubusercontent.com/u/4732915?v=3&s=128'}

E, na sequência, usar esse dicionário para passar uma variável chamada perfil para o contexto do template:

@meu_web_app.route('/')
def pagina_inicial():
    return render_template('home.html', perfil=CUDUCOS)

Por fim, vamor utilizar, ao invés das minhas informações, a variável perfil no template:

<!DOCTYPE HTML>
<html>
  <head>
    <title>{{ perfil.nome }}</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  </head>
  <body>
    <div class="container">
      <div class="jumbotron">
        <img src="{{ perfil.foto }}" alt="{{ perfil.nome }}" class="img-circle">
        <h1>{{ perfil.nome }}</h1>
        <p>{{ perfil.descricao }}</p>
        <p><a class="btn btn-primary btn-lg" href="{{ perfil.url }}"
            role="button">Me siga no {{ perfil.nome_url }}</a></p>
      </div>
    </div>
  </body>
</html>

Feito isso, temos todas as informações disponíveis no nosso ambiente Python, e não mais no HTML. E os testes nos garantem que no final das contas, para o usuário, a página não mudou — ou seja, estamos mostrando as informações corretamente.

Criando conteúdo dinâmico

Vamos agora criar um outro dicionário para termos informações de outras pessoas. E vamos juntar todos os perfis em uma variável chamada PERFIS:

MENDES = {'nome': 'Eduardo Mendes',
          'descricao': 'Apaixonado por software livre e criador de lambdas.',
          'url': 'http://github.com/z4r4tu5tr4',
          'nome_url': 'GitHub',
          'foto': 'https://avatars.githubusercontent.com/u/6801122?v=3&s=128'}

PERFIS = {'cuducos': CUDUCOS,
          'z4r4tu5tr4': MENDES}

Agora, se utilizarmos nossa pagina_principal() com o primeiro perfil, nossos testes passam. Podemos passar o outro perfil e ver, no navegador, que já temos a nossa página com outras informações:

@meu_web_app.route('/')
def pagina_inicial():
    return render_template('home.html', perfil=PERFIS['z4r4tu5tr4'])

Mas se rodarmos os testes assim, veremos duas falhas:

.F..F.
======================================================================
FAIL: test_content (__main__.TestHome)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 19, in test_content
    self.assertIn('<title>Eduardo Cuducos</title>', str(response_str))
AssertionError: '<title>Eduardo Cuducos</title>' not found in '<!DOCTYPE HTML>\n<html>\n  <head>\n    <title>Eduardo Mendes</title>\n    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">\n  </head>\n  <body>\n    <div class="container">\n      <div class="jumbotron">\n        <img src="https://avatars.githubusercontent.com/u/6801122?v=3&amp;s=128" alt="Eduardo Mendes" class="img-circle">\n        <h1>Eduardo Mendes</h1>\n        <p>Apaixonado por software livre e criador de lambdas.</p>\n        <p><a class="btn btn-primary btn-lg" href="http://github.com/z4r4tu5tr4"\n            role="button">Me siga no GitHub</a></p>\n      </div>\n    </div>\n  </body>\n</html>'

======================================================================
FAIL: test_link (__main__.TestHome)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 34, in test_link
    self.assertIn('href="http://twitter.com/cuducos"', response_str)
AssertionError: 'href="http://twitter.com/cuducos"' not found in '<!DOCTYPE HTML>\n<html>\n  <head>\n    <title>Eduardo Mendes</title>\n    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">\n  </head>\n  <body>\n    <div class="container">\n      <div class="jumbotron">\n        <img src="https://avatars.githubusercontent.com/u/6801122?v=3&amp;s=128" alt="Eduardo Mendes" class="img-circle">\n        <h1>Eduardo Mendes</h1>\n        <p>Apaixonado por software livre e criador de lambdas.</p>\n        <p><a class="btn btn-primary btn-lg" href="http://github.com/z4r4tu5tr4"\n            role="button">Me siga no GitHub</a></p>\n      </div>\n    </div>\n  </body>\n</html>'

----------------------------------------------------------------------
Ran 6 tests in 0.024s

FAILED (failures=2)

Os testes nos dizem que bagunçamos as informações. Os testes de conteúdo não encontram mais Eduardo Cuducos na página, nem o link para http://twitter.com/cuducos.

Vamos arrumar isso fazendo um caso de teste para cada perfil. Vamos mudar também nosso esquema de URL. Ao invés de testar a raíz da aplicação, vamos testar se em /nome-do-usuário vemos as informações desse usuário.

Vamos renomear TestGet para TestCuducos e mudar a URL no setUp():

class TestCuducos(unittest.TestCase):

    def setUp(self):
        app = meu_web_app.test_client()
        self.response = app.get('/cuducos')

Agora podemos duplicar toda essa classe renomeando-a para TestZ4r4tu5tr4, substituindo as informações pertinentes:

class TestZ4r4tu5tr4(unittest.TestCase):

    def setUp(self):
        app = meu_web_app.test_client()
        self.response = app.get('/z4r4tu5tr4')

    def test_get(self):
        self.assertEqual(200, self.response.status_code)

    def test_content_type(self):
        self.assertIn('text/html', self.response.content_type)

    def test_content(self):
        response_str = self.response.data.decode('utf-8')
        self.assertIn('<title>Eduardo Mendes</title>', str(response_str))
        self.assertIn('<h1>Eduardo Mendes</h1>', str(response_str))
        self.assertIn('<p>Apaixonado por software livre', str(response_str))

    def test_bootstrap_css(self):
        response_str = self.response.data.decode('utf-8')
        self.assertIn('bootstrap.min.css', response_str)

    def test_profile_image(self):
        response_str = self.response.data.decode('utf-8')
        self.assertIn('<img src="', response_str)
        self.assertIn('class="img-circle"', response_str)

    def test_link(self):
        response_str = self.response.data.decode('utf-8')
        self.assertIn('href="http://github.com/z4r4tu5tr4"', response_str)
        self.assertIn('>Me siga no GitHub</a>', response_str)

Testes prontos… e falhando, claro. Não mudamos nosso esquema de URLs no Flask. Voltemos ao app.py.

Podemos começar com algo repetitivo, mas simples:

@meu_web_app.route('/cuducos')
def pagina_inicial_cuducos():
    perfil = PERFIS['cuducos']
    return render_template('home.html', perfil=perfil)


@meu_web_app.route('/z4r4tu5tr4')
def pagina_inicial_z4r4tu5tr4():
    perfil = PERFIS['z4r4tu5tr4']
    return render_template('home.html', perfil=perfil)

Como resultado, temos nossa aplicação com conteúdo dinâmico, com testes passando e funcionando!

Podemos melhorar um pouco mais. Essa repetição dos métodos pagina_inicial_cuducos() e pagina_inicial_z4r4tu5tr4() é facilmente evitada no Flask:

@meu_web_app.route('/<perfil>')
def pagina_inicial(perfil):
    perfil = PERFIS[perfil]
    return render_template('home.html', perfil=perfil)

Agora o Flask recebe uma variável perfil depois da / (e sabemos que é uma variável pois envolvemos o nome perfil entre os sinais de < e >). E utilizamos essa variável para escolher qual perfil passar para nosso tempate.

Considerações finais

Se chegou até aqui, vale a pena ressaltar que esse post tem apenas o objetivo de introduzir a ideia básica do TDD. Ou seja: ver como o hábito, o método de programar pouco a pouco (baby steps) e sempre começando com os testes te dão dois benefícios sensacionais: eles não só garantem que a aplicação funcionará como esperado, mas eles guiam o próprio processo de desenvolvimento. As mensagens de erro te dizer – muitas vezes literalmente — o qual é a próxima linha de código que você vai escrever.

E, se chegou até aqui, talvez você queira se aprofundar nos assuntos dos quais falamos. Além de inúmeros posts aqui do blog, ressalto mais algumas referências.

Leituras recomendadas para conhecer mais sobre Flask:

Leitura recomendada para conhecer mais sobre TDD:

  • Em inglês: Livro do Harry Percival
  • Em inglês: essa resposta no Stack Overflow sobre unit, integration, functional e acceptance test.

Quem aprendeu alguma coisa nova?

— Raymond Hettinger

por Eduardo Cuducos em 14 de March de 2016 às 14:59

Álvaro Jüsten

Reading Parquet Files in Python with rows

Many people in the data science field use the parquet format to store tabular data, as it's the default format used by Apache Spark -- an efficient data storage format for analytics. The problem is: the format is binary (you can't just open it with your preferred code editor) and there's no such a good Python library to read -- not until today!

I found a Python library called parquet-python on GitHub but it's hard to use, doesn't have one code example, was not available on PyPI and it looks like it's not maintained anymore. So I decided to implement a parquet plugin (read-only) for my library rows: it uses the parquet-python library under the hood (I needed to upload it to PyPI so you can install it easly) and exposes the data in a pretty simple, pythonic way.

Installation

I didn't realese the rows version with this plugin yet, so you need to grab the most recent rows version by running:

pip install -U git+https://github.com/turicas/rows.git@develop

And also the dependency:

pip install parquet

If the data is compressed using Google's snappy you'll also need the library headers and other Python dependency -- install everything by running:

apt-get install libsnappy-dev
pip install python-snappy

Then you can use rows.import_from_parquet(filename) in your programs! \o/

Python Example

A quick Python code example:

import rows

table = rows.import_from_parquet('myfile.parquet')
for row in table:
    print row  # access fields values with `rows.field_name`

Note that the current implementation is not optimized (for example, it'll put everything into memory) but at least you can extract desired data and then convert to a more friendly format easily.

Converting Parquet to Other Formats with rows' CLI

You can convert Parquet files to many tabular formats (like CSV) by using the rows's command-line interface, so you don't need to code.

Install the rows CLI by running:

pip install rows[cli]

Now convert the parquet file:

rows convert myfile.parquet myfile.csv  # yes, simple like this!

You can replace csv with any other supported format (the list is always growing!), such as: txt, html, xls, xlsx and sqlite.

If your file is small enough you can actually see it without needing to save the output to another file by using the print subcommand:

rows print myfile.parquet  # will extract, convert and print data as text

And you can actually query data as in SQL (this CLI is awesome!), for example:

rows query 'nation_key < 10' tests/data/nation.dict.parquet \
     --output=data.csv

By running this command the CLI will:

  • Import data from tests/data/nation.dict.parquet file into memory;
  • Export to SQLite (:memory:);
  • Run the query (nation_key < 10) and get the results;
  • Convert the results to a new rows.Table object;
  • Export the table to CSV format and save it into data.csv (the result format could be html, xls, xlsx or any other write-plugin supported by rows).

With this addition to rows I think the library and its command-line interface became one of the tools every data scientist should have installed. ;-)

por noreply@blogger.com (Álvaro Justen "Turicas") em 14 de March de 2016 às 04:48

March 13, 2016

Julio Cesar Eiras Melanda

Nova turma do curso de Django com CTNovatec

Olá!

Temos novidades! Abrimos mais uma turma de Django no CTNovatec!

Tem interesse em aprender o Framework Web Python usado pelo Pinterest e pelo Instagram? Então confira aqui:

http://ctnovatec.com.br/cursos/trilha-python/curso-de-django/

por admin em 13 de March de 2016 às 00:09

March 12, 2016

Thiago Avelino

I’m. very happy to see a non-technical person thinking that way.

I hope that after a year still continue without need of them, I know it’s not easy to stop using Google (Gmail), but it is possible and…

por Avelino em 12 de March de 2016 às 12:06

I’m. very happy to see a non-technical person thinking that way.

I hope that after a year still continue without need of them, I know it’s not easy to stop using Google (Gmail), but it is possible and…

por Avelino em 12 de March de 2016 às 12:05

I wrote an article talking about the learning curve for those who want to use vim.

Your article complements it, I’m going to link there https://medium.com/@avelino0/starting-to-use-vim-6c5fb3639f80#.o3ftpimm3

por Avelino em 12 de March de 2016 às 11:25

March 11, 2016

Aprenda Python

Technical tenets

Some technical tenets, in random order, to any human programmer: - Don't create technical debts. - Forge ahead a "MUP" (Minimal Useful Product). - Understand your users' needs. - Manage and kill technical debts, because you'll create them sooner or later. - Meet the deadlines. - Live with some technical debts. - Release often. - Keep it simple, but not simplistic. - Automate

por Vinicius Assef (noreply@blogger.com) em 11 de March de 2016 às 16:44

March 10, 2016

Magnun Leno

Hack ‘n’ Cast v0.18 - Hack ‘n’ Drops #002

Microsoft Windows 10, Mycroft e uma japa maker são os tópicos desse Hack 'n' Cast.

Baixe o episódio e leia o shownotes

por Magnun em 10 de March de 2016 às 03:38

March 07, 2016

Álvaro Jüsten

Searching dd-wrt Router Database with ddwrtdb

I really like the dd-wrt router operating system: I can install it on cheap routers (starting from 27 USD, as TP-Link WR741ND) to have a great Web configuration interface and performance (way better than general factory software).

I'm always looking for new router models to check if they're supported by dd-wrt and to compare prices/hardware specs (as I'm always buying new routers to help some friends with their Wi-Fi networks). The problem is: dd-wrt's website usability is not that good, specially the router database search. As I prefer to use my terminal instead of the Web browser, I've created a command-line tool to deal with it: it's called ddwrtdb and the code is available at my GitHub account!

It's also available on Python Package Index so you can install it using Python's pip by running:

pip install https://github.com/turicas/rows/archive/develop.zip
pip install ddwrtdb

That's it! Now run ddwrtdb --help to see the available commands (it's pretty intuitive). You can also check out the project' README for command examples.

This simple command-line tool (< 200 lines of Python code) was created using these awesome libraries:

  • click, to easily create a beautiful command-line interface;
  • lxml, to use XPath in order to parse HTML more easily;
  • requests, to make HTTP requests to dd-wrt's website;
  • rows, to automatically extract tables from HTML and to export data to any tabular format.

por noreply@blogger.com (Álvaro Justen "Turicas") em 07 de March de 2016 às 02:24

March 04, 2016

Bruno Cezar Rocha

Microservices with Python, RabbitMQ and Nameko

"Micro-services is the new black" - Splitting the project in to independently scalable services is the currently the best option to ensure the evolution of the code. In Python there is a Framework called "Nameko" which makes it very easy and powerful.

Micro services

The term "Microservice Architecture" has sprung up over the last few years to describe a particular way of designing software applications as suites of independently deployable services. - M. Fowler

I recommend reading the Fowler's posts to understand the theory behind it.

Ok I so what does it mean?

In brief a Micro Service Architecture exists when your system is divided in small (single context bound) responsibilities blocks, those blocks doesn't know each other, they only have a common point of communication, generally a message queue, and does know the communication protocol and interfaces.

Give me a real-life example

Consider you have an REST API, that API has an endpoint receiving some data and you need to perform some kind of computation with that data, instead of blocking the caller you can do it asynchronously, return an status "OK - Your request will be processed" to the caller and do it in a background task.

Also you want to send an email notification when the computation is finished without blocking the main computing process, so it is better to delegate the "email sending" to another service.

Scenario

enter image description here

Show me the code!

Lets create the system to understand it in practice.

Environment

We need an environment with:

  • A running RabbitMQ
  • Python VirtualEnv for services
  • Python VirtualEnv for API

Rabbit

The easiest way to have a RabbitMQ in development environment is running its official docker container, considering you have Docker installed run:

docker run -d --hostname my-rabbit --name some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3-management

Go to the browser and access http://localhost:15672 using credentials guest:guest if you can login to RabbitMQ dashboard it means you have it running locally for development.

enter image description here

The Service environment

Now lets create the Micro Services to consume our tasks. We'll have a service for computing and another for mail, follow the steps.

In a shell create the root project directory

$ mkdir myproject
$ cd myproject

Create and activate a virtualenv (you can also use virtualenv-wrapper)

$ virtualenv service_env
$ source service_env/bin/activate

Install nameko framework and yagmail

(service_env)$ pip install nameko
(service_env)$ pip install yagmail

The service code

Now having that virtualenv prepared (consider you can run service in a server and API in another) lets code the nameko RPC Services.

We are going to put both services in a single python module, but you can also split in separate modules and also run them in separate servers if needed.

In a file called service.py

import yagmail
from nameko.rpc import rpc, RpcProxy


class Mail(object):
    name = "mail"

    @rpc
    def send(self, to, subject, contents):
        yag = yagmail.SMTP('myname@gmail.com', 'mypassword')
        # read the above credentials from a safe place.
        # Tip: take a look at Dynaconf setting module
        yag.send(to, subject, contents)


class Compute(object):
    name = "compute"
    mail = RpcProxy('mail')    

    @rpc
    def compute(self, operation, value, other, email):
        operations = {'sum': lambda x, y: x + y,
                      'mul': lambda x, y: x * y,
                      'div': lambda x, y: x / y,
                      'sub': lambda x, y: x - y}
        try:
            result = operations[operation](value, other)
        except Exception as e:
            self.mail.send.async(email, "An error occurred", str(e))
            raise
        else:
            self.mail.send.async(
                email, 
                "Your operation is complete!", 
                "The result is: %s" % result
            )
            return result

Now with the above services definition we need to run it as a Nameko RPC service.

NOTE: We are going to run it in a console and leave it running, but in production it is recommended to put the service to run using supervisord or an alternative.

Run the service and let it running in a shell

(service_env)$ nameko run service --broker amqp://guest:guest@localhost
starting services: mail, compute
Connected to amqp://guest:**@127.0.0.1:5672//
Connected to amqp://guest:**@127.0.0.1:5672//

Testing it

Go to another shell (with the same virtenv) and test it using nameko shell

(service_env)$ nameko shell --broker amqp://guest:guest@localhost
Nameko Python 2.7.9 (default, Apr  2 2015, 15:33:21) 
[GCC 4.9.2] shell on linux2
Broker: amqp://guest:guest@localhost
>>>

You are now in the RPC client testing shell exposing the n.rpc object, play with it

>>> n.rpc.mail.send("name@email.com", "testing", "Just testing")

The above should sent an email and we can also call compute service to test it, note that it also spawns an async mail sending with result.

>>> n.rpc.compute.compute('sum', 30, 10, "name@email.com")
40
>>> n.rpc.compute.compute('sub', 30, 10, "name@email.com")
20
>>> n.rpc.compute.compute('mul', 30, 10, "name@email.com")
300
>>> n.rpc.compute.compute('div', 30, 10, "name@email.com")
3

Calling the micro-service through the API

In a different shell (or even a different server) prepare the API environment

Create and activate a virtualenv (you can also use virtualenv-wrapper)

$ virtualenv api_env
$ source api_env/bin/activate

Install Nameko, Flask and Flasgger

(api_env)$ pip install nameko
(api_env)$ pip install flask
(api_env)$ pip install flasgger

NOTE: In api you dont need the yagmail because it is service responsability

Lets say you have the following code in a file api.py

from flask import Flask, request
from flasgger import Swagger
from nameko.standalone.rpc import ClusterRpcProxy

app = Flask(__name__)
Swagger(app)
CONFIG = {'AMQP_URI': "amqp://guest:guest@localhost"}


@app.route('/compute', methods=['POST'])
def compute():
    """
    Micro Service Based Compute and Mail API
    This API is made with Flask, Flasgger and Nameko
    ---
    parameters:
      - name: body
        in: body
        required: true
        schema:
          id: data
          properties:
            operation:
              type: string
              enum:
                - sum
                - mul
                - sub
                - div
            email:
              type: string
            value:
              type: integer
            other:
              type: integer
    responses:
      200:
        description: Please wait the calculation, you'll receive an email with results
    """
    operation = request.json.get('operation')
    value = request.json.get('email')
    other = request.json.get('other')
    email = request.json.get('email')
    msg = "Please wait the calculation, you'll receive an email with results"
    subject = "API Notification"
    with ClusterRpcProxy(CONFIG) as rpc:
        # asynchronously spawning and email notification
        rpc.mail.send.async(email, subject, msg)
        # asynchronously spawning the compute task
        result = rpc.compute.compute.async(operation, value, other, email)
        return msg, 200

app.run(debug=True)

Put the above API to run in a different shell or server

(api_env) $ python api.py 
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

and then access the url http://localhost:5000/apidocs/index.html you will see the Flasgger UI and you can interact with the api and start producing tasks on queue to the service to consume.

[image]

NOTE: You can see the shell where service is running for logging, prints and error messages. You can also access the RabbitMQ dashboard to see if there is some message in process there.

There is a lot of more advanced things you can do with Nameko framework you can find more information on https://nameko.readthedocs.org/en/stable/

Let's Micro Serve!

por Bruno Rocha em 04 de March de 2016 às 20:53

February 28, 2016

Álvaro Jüsten

Detectando Encoding e Tipo de Arquivo com Python

Read this blog post in English.

O conteúdo desse artigo está disponível também em vídeo:

Alguns dos usuários da minha biblioteca rows me pediram funcionalidades de detecção de encoding e tipo de arquivo, então comecei a procurar alguma biblioteca Python simples e rápida que desse conta dessa tarefa. O problema: encontrei muitas bibliotecas e nenhuma delas me atraiu por alguns dos seguintes motivos:

  • Não tinha uma implementação pythônica;
  • Não estava disponível no Debian (se eu usasse o pacote rows no Debian iria quebrar);
  • Não estava sendo mantida atualmente;
  • Não funcionava corretamente.

Então percebi que não existe uma "solução de ouro" para esse problema em Python. Muitos pythonistas usam a biblioteca chardet, mas ela detecta muitas vezes com erros e é razoavelmente lenta (principalmente se você precisar fazer a detecção enquanto um usuário aguarda a resposta através de uma chamada de API via HTTP) -- veja mais detalhes sobre isso no vídeo.

But there should be one -- and preferably only one -- obvious way to do it.

Então, eu pensei: por que não usar o programa file, que já é bastante conhecido por hackers do mundo UNIX, é rápido e muito mais assertivo que todas as outras opções? Para minha surpresa não existia um bom binding de Python para a libmagic (algumas bibliotecas rodavam o comando file mas isso não era uma opção para mim pois dependia de mais um pacote do sistema e não era uma solução muito portável).

Depois de vasculhar o repositório de código do file eu encontrei um binding simples para Python, que na época não estava disponível no PyPI e não era tão pythônica como eu esperava.

A Solução

Como o código é software livre, eu criei uma issue no sistema de bugs do file para resolver o problema e o Christos Zoulas (atual mantenedor) me pediu um patch, que eu implementei, enviei e foi aceito. :-)

Fiquei muito feliz de poder colaborar com um software importante e que eu uso desde de meus primeiros passos no mundo GNU/Linux (2003? 2004?)! Durante minha busca eu descobri que o primeiro commit da versão livre do file é de 1987 (eu tinha menos de 4 meses de idade!) -- e o software ainda é mantido hoje.

Agora todos podemos usar binding oficial do file para Python: a nova biblioteca é chamada file-magic e pode ser instalada executando:

pip install file-magic

Ela disponibiliza várias funções e atributos, mas as mais importantes são bem simples e intuitivas: elas retornam uma namedtuple com os resultados da detecção. Vamos ao código!

>>> import magic

>>> # Você pode especificar diretamente um nome de arquivo
>>> filename_detected = magic.detect_from_filename('turicas.jpg')
>>> print(filename_detected)
FileMagic(mime_type='image/jpeg', encoding='binary',
          name='JPEG image data, JFIF standard 1.02, aspect ratio, density 1x1, segment length 16, progressive, precision 8, 842x842, frames 3')
>>> # E você pode acessar os atributos da `namedtuple`:
>>> print(filename_detected.mime_type)
image/jpeg

# E se você já tem o conteúdo do arquivo em memória:
>>> with open('data.html') as fobj:
...     data = fobj.read()
>>> content_detected = magic.detect_from_content(data)
>>> print(content_detected)
FileMagic(mime_type='text/html', encoding='utf-8',
          name='HTML document, UTF-8 Unicode text')
>>> print(content_detected.encoding)
utf-8

Algumas coisas ainda precisam ser melhoradas (como a documentação, rodar os testes em outras plataformas etc.), porém a biblioteca já pode ser instalada através do pip, é rápida e precisa. :-)

Espero que vocês tenham gostado! ;-)

por noreply@blogger.com (Álvaro Justen "Turicas") em 28 de February de 2016 às 18:14