Planeta PythonBrasil

PythonBrasil[10]

May 01, 2018

Lauro Moura

C++ para Pythonistas – Introdução e iteradores

C++ pode ter a fama de ser uma linguagem com complexidade comparável à legislação tributária brasileira, mas ao mesmo tempo é uma linguagem extremamente poderosa, podendo ser usada tanto desde microcontroladores até super computadores, sem contar satélites espaciais. Tamanha complexidade pode assustar quem vem de linguagens mais simples como Python (malloc? rvalue references?). Uma ajuda para tratar essa sensação de “como que eu faço aquilo?”  pode ser conhecer as ferramentas que a linguagem oferece em comparação com o que já estamos acostumados.

Nessa série de posts vamos mostrar uma visão geral de algumas dessas ferramentas de C++ e comparar como seria a funcionalidade equivalente em Python. Mais especificamente, a STL – Standard Template Library – e seu header <algorithm>. Esse header possui várias funções que podem simplificar o código e deixar o programador que vem de Python mais à vontade, sem ter que fazer tanta coisa “na mão”. Mas antes de entrar na STL propriamente dita, é importante conhecer o que são os templates de C++, que são o bloco fundamental da STL.

Antes de tudo, um aviso: Dadas as diferenças das linguagens, certamente existirão formas mais eficientes de aplicar alguns conceitos de C++ do que uma mera conversão 1-1 de código em Python, especialmente no que diz respeito ao gerenciamento de recursos (RAII vs gc vs mallocs, etc). Longe de ser um tratado de como extrair o máximo de C++, o objetivo destes posts é apenas facilitar um pouco a vida dos expatriados.

Templates

Templates são uma ferramenta muito útil para programação genérica, permitindo reuso de código de uma maneira mais segura. Numa analogia bem grosseira, eles podem ser vistos como uma espécie de “duck typing em tempo de compilação” de C++. Duck typing no sentido de que, diferente de uma função ou classe normal onde o desenvolvedor já escreve explicitamente quais são todos os tipos envolvidos, num template alguns tipos podem ficar em aberto, para serem definidos posteriormente. O usuário do template então escreve seu código usando ele normalmente e o compilador cuida de checar se todos os tipos fornecidos para o template implementam as operações necessárias para ele ser gerado.

Por exemplo, a função add abaixo recebe como parâmetro do template um tipo T, os dois parâmetros da função são do tipo T e o retorno também é do tipo T. Dentro, é chamado o operador +() para os dois parâmetros T. Como o exemplo mostra, ela pode ser usada tanto para inteiros como para strings:

#include <iostream> // Para imprimir na tela

template <class T>
T add(T a, T b)
{
    return a + b; // operator +()
}

int main()
{
   int x = 5;
   int y = 4;
   std::string name = "Monty";
   std::string surname = "Python";

   std::cout << add(x, y) << std::endl;
   std::cout << add(name, surname) << std::endl;
   return 0;
}

Templates são o cerne da STL, usados por exemplo, nos containers, para definir o tipo a ser guardado nas coleções (std::vector<T>, std::map<K, V>). Além disso, também são usados em coisas mais esotéricas de C++ como Template Metaprogramming, onde as features de template são usadas para efetivamente rodar programas dentro do compilador e estão muito além do escopo dessa série.

Uma diferença entre os templates C++ e as funções normais de Python está no código gerado. Em python, você normalmente só tem uma única instância da função e a máquina virtual faz o “duck typing” em tempo de execução, chamando os métodos dos tipos apropriados. Mas como o código em C++ é convertido diretamente em código executável, sem um interpretador ou máquina virtual, o compilador na prática efetua a substituição dos tipos nos templates e gera uma função “anônima” para aquela substituição (conjunto de tipos). Isso é a chamada instanciação de templates. Ou seja, um eventual template add<T>(T a, T b) que seja chamado para T = int e T = std::string vai gerar duas funções, algo como __add_int(int a, int b) e __add_string(std::string a, std::string b). Por isso que algums sistemas que abusam demais de templates podem acabar ficando grandes demais se não tivermos cuidado.

STL: A Biblioteca Padrão de Arcabouços Templates

A STL é um dos pilares do C++ moderno. E ela pode ser dividida em 4 principais componentes: Containers, Iteradores, Funções e Algoritmos.

Os containers são classes que armazenam elementos, cuidando do gerenciamento de memória usado para guardar os mesmos e oferecendo uma interface uniforme de acesso através de iteradores.  Entre os containers oferecidos estão listas (std::list), mapas (std::map), filas de prioridade (std::priority_queue) e outros. Os containers são implementados como templates para facilitar o reuso deles para diferentes tipos de objetos a serem armazenados.

Iteradores (descritos em maior profundidade mais abaixo) são formas de acessar os items de um container de maneira uniforme, sem se preocupar tanto com o container específico. Por exemplo, ao iterar uma sequencia de itens, você pode acessar da mesma maneira tanto uma std::list quanto um std::vector. Numa analogia com Python, os containers seriam iterables e os iterators seria, bem, iterators.

Funções são representadas principalmente pelos function objects, classes que fazem overload do operator (), semelhante ao método __call__(). Isso permite por exemplo você fornecer predicados para as funções de algoritmo de maneira mais simples. Um functor pode armazenar um contexto mais apropriado para um determinado predicado do que um mero ponteiro de função ou um lambda. Alguns dos tipos de funções mais utilizados pela STL são os Predicate (funções unárias retornando booleano sem modificar o argumento) e os BinaryPredicate (funções binárias retornando booleano sem modificar o argumento). Imagine std::function (a principal classe function object) como sendo poder passar funções C++ como um objeto qualquer, como em Python.

Algoritmos por sua vez fazem uso extensivo desses containers, iteradores e funções para implementar diversas funcionalidades, como map, reduce, produto interno, merge, heap, etc.

Iteradores

Na prática, iteradores são uma ferramenta que permite acessar o conteúdo de containers como mapas, listas, vetores, strings sem se importar diretamente com a estrutura de dados por baixo. Um iterador pode ser visto como um ponteiro que num dado momento referencia algum item do container. A semelhança é tanta com ponteiros que o operator * é usado para acessar o conteúdo de um iterator, tal qual ponteiros.

Os iterators se agrupam em de acordo com suas funcionalidades, por exemplo, se permitem só leitura (Input iterators), se os dados podem ser acessados randomicamente (Random Access iterators) ou sequencialmente (Forward / Bidirectional iterators), se permite modificação ou não (const vs non-const iterators), etc.

Normalmente, os dois métodos mais utilizadas para acessar os iterators de um container são begin() e end(). O primeiro retorna um iterador para o primeiro elemento do container, enquanto a segunda retorna uma posição além do final do container (ambas formando o intervalo [begin, end), assim como o range() de Python).

range-begin-end

(Fonte da imagem: Método begin())

Um dos usos mais comuns de iteradores em C++ é nos for loops. Tradicionalmente, os loops em C/C++ com iteradores possuem a forma:

for (auto it = cont.begin(); it != cont.end(); it++) {...}

Com C++11, os range loops permitem usar uma sintaxe mais enxuta (e parecida com o for…in de Python):

for (auto&& it : cont) {...}

Na prática, é apenas um açúcar sintático para primeira forma, onde o compilador automaticamente declara, compara e incrementa as variáveis. Se cont for uma classe que possui os métodos begin() e end() – como vários containers –  eles serão utilizados para inicializar e comparar os iteradores.

No próximo post, começaremos a destrinchar o <algorithm> comparando com as funções análogas de Python.

o/

por lauro em 01 de May de 2018 às 02:10

April 25, 2018

Humberto Rocha

TLDR: Configurando corretamente o default de um Model Field do Django

Esta postagem é o resultado de um bug que me assombrou por uns três meses até conseguir isolar totalmente o erro e, por acontecer em condições muito específicas de temperatura e pressão, tomou muito tempo quebrando cabeça até perceber que o problema era este.

Disclaimer: essa informação existe na documentação do Django, porém reforçá-la pode lhe poupar um belo tempo de debugging.

Um dos atributos básicos de um Model field do Django é o default que determina o valor padrão de um campo quando criamos uma instância do Model. Você pode atribuir um valor a ele ou um callable que nada mais é que uma função ou uma classe que implemente o método __call__.

Agora vem a dica importante:

Nunca atribua um valor mutável a este campo. Pois isso irá fazer com que todas as instâncias deste model que foram criadas em uma mesma execução do seu código compartilhem aquela mesma instância do objeto mutável. Isso vale para dicionários, listas, sets e qualquer outro objeto mutável.

Quando quiser atribuir um deles como default passe o callable como parâmetro:

# Errado
class SomeClass(models.Model):
    custom_data = JSONField(default={})

# Certo
class SomeClass(models.Model):
    custom_data = JSONField(default=dict)

O maior problema deste erro é que ele muitas vezes é silencioso e só aparece em alguma sequência de criações e pode te deixar muito confuso.

Bônus

Funções lambda não podem ser usadas como default pois elas não conseguem ser serializadas na hora de gerar suas migrations, se precisar de um valor mais elaborado crie uma função e passe ela como argumento. Mas, lembre-se de passar a função como argumento (ex.: default=create_default), não o resultado dela (ex.: default=create_default()).

por Humberto Rocha em 25 de April de 2018 às 00:00

April 19, 2018

Magnun Leno

Algumas Razões Para Amar o PostgresSQL

Geralmente, quando se fala em SGBDs OpenSource, a primeira resposta que se ouve é MySQL/MariaDB. Eu sempre torço meu nariz para respostas como essa… Implicancia pessoal? Talvez um pouco, mas existem muitos fundamentos

Algumas Razões Para Amar o PostgresSQL é um artigo original de Mind Bending

por Magnun em 19 de April de 2018 às 18:02

April 15, 2018

Humberto Rocha

Autenticação JWT com Angular e Django

Quase todo sistema que opera na internet e armazena dados de usuário hoje em dia depende de uma camada de autenticação. E com o crescimento do modelo de API`s e separação do frontend do backend a implementação desta camada cresceu em complexidade.

Esta postagem tem como foco mostrar o caminho das pedras em uma stack de frontend Angular 5 e backend Django 2 utilizando o modelo de separação discutido na postagem "Separando o Frontend do Backend com Angular e Django" .

Autenticação

Existem diversas formas de se autenticar em um sistema. A mais famosa e adotada pela internet como padrão é o modelo de usuário e senha, onde apresentamos nosso identificador (id, nome de usuário, email ou semelhante) e um segredo compartilhado com o serviço que queremos acessar, que após sua validação, nos é devolvido um token que funciona como um bilhete de acesso aos nossos recursos pessoais naquele serviço.

Quando implementamos sistemas monolíticos onde o backend e o frontend estão juntos esta manutenção deste token acontece de forma automática pela maioria das bibliotecas de autenticação disponíveis por aí. No máximo decidimos coisas como tempo de validade da sessão ou onde o token será armazenado trocando uma variável de configuração ou outra.

Porém, ao separarmos o backend do frontend esta gestão fica um pouco mais por nossa conta as decisões sobre o que fazer e usar para isso deve ser tomadas por nós durante a implementação. E existem enumeras formas de se fazer esta gestão de acesso, uma delas é simplesmente utilizar o token padrão devolvido pelo sistema, mas neste caso como eles estão separados perdemos a possibilidade de atualizar a validade deste token nos obrigando a colocar novamente a senha para obter um novo acesso.

Para nos auxiliar com estes problemas utilizaremos o JWT.

JWT

JWT ou JSON Web Token nada mais é que um objeto JSON definido na RFC 7519 para realizar transferência informação de permissões de acesso entre duas pontas. Ele é codificado e assinado e possuí o seguinte formato:

header.payload.signature

No header (cabeçalho) ficam os dados do token, que informam seu tipo e o algoritmo utilizado em sua assinatura:

{
  "alg": "HS256",
  "typ": "JWT"
}

No payload (carga) ficam os dados do usuário e alguns metadados como a expiração do token:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Finalmente na signature (assinatura) os dados de header e payload codificados em base 64 e unidos por . (ponto) para serem assinados usando o algoritmo definido no header:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

Com a assinatura é possível verificar se o token não foi alterado no caminho, garantindo sua integridade. Com ela também é possível confirmar a autenticidade de sua fonte.

Estes três blocos unidos por . (ponto) cada um codificado em base 64 compõem o JWT Token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PcmVIPbcZl9j7qFzXRAeSyhtuBnHQNMuLHsaG5l804A

Após confirmar os dados de usuário e receber o JWT ele deve ser armazenado, normalmente em local storage para ser utilizado nas requisições autenticadas usando o esquema de cabeçalho JWT:

Authorization: JWT <token>

Este é um mecanismo de autenticação que não guarda estado e por não dependerem de token de sessão, não há necessidade de se preocupar com CORS.

Agora, com todo este conhecimento é hora da implementação!!!

coding cat

E para isso, utilizaremos como base a aplicação desenvolvida na postagem anterior "Separando o Frontend do Backend com Angular e Django".

Backend

A primeira coisa que faremos é instalar o pacote djangorestframework-jwt:

$ pip install djangorestframework-jwt

Em seguida iremos adicionar suas configurações no settings.py:

from datetime import timedelta

# REST Framework configs
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
    'NON_FIELD_ERRORS_KEY': 'global',
}

# JWT settings
JWT_AUTH = {
    'JWT_ALLOW_REFRESH': True,
    'JWT_EXPIRATION_DELTA': timedelta(days=2),
}

Seguido das rotas de login e atualização de token jwt em urls.py:

from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token

urlpatterns = [
    ...

    path('login/', obtain_jwt_token),
    path('refresh-token/', refresh_jwt_token),
]

Como foi alterada a permissão padrão do sistema no settings.py para IsAuthenticated ao tentar acessar a lista de compra nos deparamos com a seguinte mensagem:

denied

Agora que o acesso está sendo restrito a usuários logados chegou a hora de implementar a autenticação no frontend.

Frontend

A primeira tarefa a ser executada no frontend é a de mover o conteúdo de app.component.ts para um componente separado e adicionar rotas na aplicação, criando um segundo componente para o login:

// app.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <router-outlet></router-outlet>
  `
})
export class AppComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }
}

// login.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-login',
  template: `
  <div style="text-align:center">
    <h1>
      Login
    </h1>
  </div>

  <input #username type='text' placeholder='username'>
  <input #password type='password' placeholder='password'>
  <button (click)="login(username.value, password.value)">login</button>
  {{ error?.message }}
  `
})
export class LoginComponent implements OnInit {

  error: any;

  constructor() { }

  ngOnInit() {
  }

  login(username: string, password: number) {
    // TODO: call login
  }
}

// app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { LoginComponent } from './login.component';
import { ListComponent } from './list.component';

const routes: Routes = [
  { path: '', redirectTo: 'login', pathMatch: 'full' },
  { path: 'login', component: LoginComponent },
  { path: 'list', component: ListComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

// app.module.ts

...
import { AppRoutingModule } from './app-routing.module';
import { ListComponent } from './list.component';
import { LoginComponent } from './login.component';
...

@NgModule({
  declarations: [
    AppComponent,
    ListComponent,
    LoginComponent,
  ],
  imports: [
    ...

    AppRoutingModule,
  ],
  ...
})
export class AppModule { }

Para auxiliar na implementação do frontend utilizaremos duas bibliotecas:

$ npm install -s moment
$ npm install -s jsonwebtoken
$ npm install -s @types/jsonwebtoken

A biblioteca moment facilitará o trabalho com tempo, já que precisamos controlar a expiração do token e sua renovação enquanto a biblioteca jsonwebtoken cuidará do token em si.

Com as bibliotecas em mãos iniciaremos pelo service de autênticação:

// auth.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { CanActivate, Router } from '@angular/router';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/shareReplay';

import * as jwt from 'jsonwebtoken';
import * as moment from 'moment';

import { environment } from '../environments/environment';

@Injectable()
export class AuthService {

  private apiRoot = 'http://localhost:8000/';

  constructor(private http: HttpClient) { }

  private setSession(authResult) {
    const token = authResult.token;
    const payload = <JWTPayload> jwt.decode(token);
    const expiresAt = moment.unix(payload.exp);

    localStorage.setItem('token', authResult.token);
    localStorage.setItem('expires_at', JSON.stringify(expiresAt.valueOf()));
  }

  get token(): string {
    return localStorage.getItem('token');
  }

  login(username: string, password: string) {
    return this.http.post(
      this.apiRoot.concat('login/'),
      { username, password }
    ).do(response => this.setSession(response)).shareReplay();
  }

  logout() {
    localStorage.removeItem('token');
    localStorage.removeItem('expires_at');
  }

  refreshToken() {
    if (moment().isBetween(this.getExpiration().subtract(1, 'days'), this.getExpiration())) {
      return this.http.post(
        this.apiRoot.concat('refresh-token/'),
        { token: this.token }
      ).do(response => this.setSession(response)).shareReplay().subscribe();
    }
  }

  getExpiration() {
    const expiration = localStorage.getItem('expires_at');
    const expiresAt = JSON.parse(expiration);

    return moment(expiresAt);
  }

  isLoggedIn() {
    return moment().isBefore(this.getExpiration());
  }

  isLoggedOut() {
    return !this.isLoggedIn();
  }
}

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('token');

    if (token) {
      const cloned = req.clone({
        headers: req.headers.set('Authorization', 'JWT '.concat(token))
      });

      return next.handle(cloned);
    } else {
      return next.handle(req);
    }
  }
}

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) { }

  canActivate() {
    if (this.authService.isLoggedIn()) {
      this.authService.refreshToken();

      return true;
    } else {
      this.authService.logout();
      this.router.navigate(['login']);

      return false;
    }
  }
}

interface JWTPayload {
  user_id: number;
  username: string;
  email: string;
  exp: number;
}

O service criado possuí 3 classes e 1 interface das quais suas funções são as seguintes:

A classe AuthService é a responsável por no autenticar de fato no sistema. Ela implemanta as funções de login, logout e refreshToken para fazer a manutenção da sessão no sistema, auxiliada das funções setSession que salva a sessão em local storage, getExpiration que realiza o cálculo de expiração para a função refreshToken decidir se é hora de atualizar o token ou não, os pares isLoggedIn e isLoggedOut que são utilizados para verificar se o usuário está logado e o getter token que retorna o JWT para ser utilizado nas requisições autenticadas.

A classe AuthInterceptor implementa os interceptadores do Angular, que neste caso intercepta todas as requisições http realizadas e, caso o usuário esteja logado, injeta o cabeçalho Authorization JWT <token> na requisição para realizar chamadas autenticadas na API.

A classe AuthGuard serve como um escudo que impede o acesso de usuário não logado nas rotas em que ela for vinculada, se um usuário não logado tenta acessar determinada rota protegida por ela, o mesmo será redirecionado para a tela de login.

E por fim a interface JWTPayload que serve somente para definirmos o formato do payload retornado no JWT dentro do typescript.

Após a criação do service vamos adicioná-lo na aplicação, definir as rotas protegidas e finalmente realizar a chamada de autenticação no componente de login:

// app.module.ts

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthService, AuthInterceptor, AuthGuard } from './auth.service';

@NgModule({
  ...
  providers: [
    ...
    AuthService,
    AuthGuard,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true,
    },
  ],
})
export class AppModule { }

// app-routing.module.ts

import { AuthGuard } from './auth.service';
...

const routes: Routes = [
  ...
  { path: 'list', component: ListComponent, canActivate: [AuthGuard] },
];

// login.component.ts

import { Router } from '@angular/router';
import { AuthService } from './auth.service';

...
export class LoginComponent implements OnInit {

  error: any;

  constructor(
    private authService: AuthService,
    private router: Router,
  ) { }

  ngOnInit() {
  }

  login(username: string, password: string) {
    this.authService.login(username, password).subscribe(
      success => this.router.navigate(['list']),
      error => this.error = error
    );
  }
}

Com a aplicação rodando agora podemos visualizar processo de login funcionando \o/:

auth

it works

Conclusão

Já existem bibliotecas no Angular que implementam a autenticação usando JWT automaticamente, porém, compreender o funcionamento deste mecanismo que estará presente durante toda a vida útil da sua aplicação e ter a autonomia para fazer os ajustes necessários de acordo com suas necessidades faz com que uma implementação um pouco mais manual valha a pena o esforço.

por Humberto Rocha em 15 de April de 2018 às 00:00

April 09, 2018

Flavio Ribeiro

Trusting the Magic of Beginnings

Trusting the Magic of Beginnings

A few years ago, when moving out from my home country, lots of things happened and my life changed in literally all different fronts.

I had the chance to contribute to a couple of projects that impacted the whole video publishing lifecycle at The Times while helping to shape a culture of sharing & openness. And I'm so proud of our achievements!

Trusting the Magic of Beginnings

morning Times from @mynytimes
My Journey in a Nutshell

I started by helping on the removal of flash-related components (1-2-3) of VHS, The Times internal player.

After that, we created a new team to work on back-end services in order to fix and improve the video publishing workflow. I took the opportunity to learn Go (4) and worked with my teammates in a new transcoding pipeline (5-6). After that, as part of the company's strategy, we migrated our system and video assets to the cloud, and in my opinion, we did in a pretty clever way (7).

We also integrated closed captions generation and playback (8-9) on our video library, benefiting The Times users and opening up the company to a bunch of new business opportunities. During this period, we could also create a new platform for live streaming (10) that was used by the newsroom to cover huge events such as the American Presidential Election debates and correlated events.

Trusting the Magic of Beginnings

Enjoying Demuxed with Said and Thompson on Crunchyroll HQ

I had the support of the company to present some projects in conferences and meetups (11-12-13-14-15). This allowed me to grow my presentation skills (and I know there is a wide room for improvement here) and create a nice network of contacts among our tech community. I am sincerely grateful for everything The New York Times has done for me in this regard.

On the personal side, I feel that I have grown so much! All my coworkers and friends read and consume all kinds of media all the time, whether they are podcasts, on-demand videos, live channels, newspapers or mailing lists and this pushed me to do the same.

This practice came into my daily life and made me even more passionate about the world of media, including how big conglomerates and small startups are positioned on this huge market. I would like to thank all those who have encouraged me and influenced me to become even more involved in this world!

Coming Next

Today I'm joining another world-class media vehicle, CBS Interactive.

CBS Interactive, a division of the CBS Corporation, is the premier online content network for information and entertainment. With over 1 billion users visiting their properties every quarter, CBS Interactive is a global top 10 web property and the largest premium content network online.

I'm very enthusiastic about what's coming down the line. I really hope to make an impact on the projects, make lots of new friends and bring the best of me for this company and all associated business units.

I also hope to keep pushing great things to production, attend to conferences, open source stuff and help on growing the video-dev (16) community.

For those who made it to the end of this post and are somehow part of the projects we worked, thank you so much for everything. I learned more than ever during this time. I really appreciate all types of learnings and experiences you guys provided to me. Wish me luck!

por Flávio Ribeiro em 09 de April de 2018 às 14:38

April 05, 2018

Aprenda Python

* Entendendo virtualenvs

window.location = "http://blog.aprendapython.com.br/articles/entendendo-virtualenv/"; Se você é iniciante em Python, provavelmente já ouviu falar em _virtualenv_. Todo mundo aconselha usar, mas para que serve um _virtualenv_? Que problema ele resolve, afinal? O que é um virtualenv? ---------------------- Em poucas palavras, o virtualenv permite que várias versões de uma mesma biblioteca

por Vinicius Assef (noreply@blogger.com) em 05 de April de 2018 às 19:54

* Por que properties são melhores que getters e setters

window.location = "http://blog.aprendapython.com.br/articles/pq-properties-sao-melhores-que-getters-and-setters/"; Quando vemos uma classe em Python escrita com métodos getters e setters, logo percebemos que o programador ainda não "pegou o jeito" de OOP em Python. Getters e setters são muito comuns em outras linguagens de programação porque os atributos normalmente são declarados com escopo

por Vinicius Assef (noreply@blogger.com) em 05 de April de 2018 às 19:31

* Como hospedar meu site feito em Python?

window.location = "http://blog.aprendapython.com.br/articles/hospedar-site-python/"; Vou começar esse artigo pelo óbvio, mas que gera frustração em quem vem do PHP: hospedar Python é mais complicado do que hospedar PHP. Mas não é o fim do mundo. Pronto, falei. Agora, vamos seguir pelos itens básicos necessários para hospedar um projeto em Python. Servidor web ------------ Não tem jeito

por Vinicius Assef (noreply@blogger.com) em 05 de April de 2018 às 19:26

April 02, 2018

Thiago Avelino

Guia de estilo para pacotes Go

Tradução do blogpost Style guideline for Go packages escrito pela @rakyll.

One code style to rule all

Código organizado em Go é fácil de entender, usar e ler. A falta de organização em código Go é tão crítico quanto as APIs má projetadas. Os diretórios, nome e a estrutura dos seus pacotes são os primeiros elementos com os quais os utilizadores vêem e interagem.

O objetivo deste blogpost é ajudá-lo com boas práticas comuns para não definir regras ruim. Você deve sempre usar o seu julgamento para escolhe a solução mais elegante para sua implementação.

Continue lendo em avelino.xxx.

por Avelino em 02 de April de 2018 às 17:06

March 27, 2018

Thiago Avelino

Fazer o melhor que podemos na condição que tem — Monja Coen

Esta fazendo o possível ou o melhor? Se você (ou eu) podendo fazer o meu melhor me contato com o possível cairemos em um lugar chamado mediocridade

Continue lendo em avelino.xxx.

por Avelino em 27 de March de 2018 às 14:58

March 26, 2018

Thiago Avelino

Empreendendo em seu local de trabalho

Em 2014 palestrei em alguns evendo falando sobre Empreender em seu local de trabalho, muitas pessoas pensam que empreender é abrir uma empresa (ter CNPJ) e cuidar de questões burocraticas de ter uma empresa (contabilidade, fluxo de caixa e blá blá blá), mas na real o que é empreender?

Porque esse blogpost, após 3 anos de palestra?

Continue lendo em avelino.xxx.

por Avelino em 26 de March de 2018 às 13:11

March 19, 2018

Ricbit

A Marcha dos Lemmings

Lemmings são pequenos roedores que vivem na tundra ártica. Eles possuem pelos longos e macios, rabo curto, são herbívoros e explodem.

Sim, eles explodem! Os lemmings possuem a capacidade de ligar um contador luminoso sobre suas cabeças. A cada passo que dão, o contador decrementa, até que finalmente explodem quando o contador chega a zero.


O que você faria se soubesse que vai explodir em seis passos? Parece que não tem muitos passeios possíveis com exatamente seis passos, mas a intuição é meio falha aqui. Na verdade, o número de passeios possíveis é exponencial no número de passos! Como fazer um algoritmo que calcula o número de caminhos possíveis que o lemming pode fazer antes de explodir?

As Regras do Jogo


Para modelar o problema, vamos supor que os passos que lemming pode fazer são descritos por um grafo orientado. Por exemplo, no grafo abaixo o lemming começa no nó A. Do A ele pode ir para B ou C, de B ele pode ir para C, e de C ele pode ir para A.


Se o nosso lemming pode dar seis passos, então existem sete caminhos diferentes que ele pode fazer:

ABCABCA
ABCACAB
ABCACAC
ACABCAB
ACABCAC
ACACABC
ACACACA

Logo ##p(6)=7##, e podemos até tabelar ##p(n)## para todos os ##n## de 0 a 6:

$$p(0..6)=1,2,2,3,4,5,7$$
Dado ##n##, como fazer um algoritmo que calcule ##p(n)##?

Eu vou mostrar três algoritmos possíveis usando a linguagem do Wolfram Mathematica. Para isso, o grafo precisa ser descrito de alguma maneira que os algoritmos entendam. Eu vou usar uma matriz de adjacências: para um grafo com ##k## nós, a matriz é ##k\times k## e o elemento na linha ##i## da coluna ##j## é 1 se existe uma ligação saindo do nó ##j## e indo em direção ao nó ##i##, e 0 em caso contrário. Para o nosso exemplo, a matriz é:

$$M=\left[\begin{matrix}0 && 0 && 1 \\ 1 && 0 && 0 \\ 1 && 1 && 0\end{matrix}\right]$$

Força Bruta


Sempre que eu tenho que fazer um algoritmo do zero, prefiro começar com a força bruta. É fácil e rápido de implementar, e embora não seja eficiente, serve para conferir o resultado dos algoritmos mais avançados.

Nesse caso, para implementar a força bruta você passa o grafo, o nó em que o lemming está, quantos passos faltam para terminar, e o resto é uma recursão simples. Como toda recursão, não esqueça de fazer o caso base! Para esse problema, o caso base é ##p(0)=1##, ou seja, tem um único caminho possível sem dar passo nenhum, que é não sair do lugar.

No fim, a solução em força bruta fica assim:
count[graph_, pos_, 0] := 1
count[graph_, pos_, size_] := Sum[
If[graph[[i, pos]] == 1, count[graph, i, size - 1], 0],
{i, 1, Length[graph]}]
Vamos conferir o resultado:
graph := {{0, 0, 1}, {1, 0, 0}, {1, 1, 0}}
Print[Table[count[graph, 1, n], {n, 0, 6}]]

{1,2,2,3,4,5,7}
Perfeito! Mas qual é a complexidade desse algoritmo? Bem, ele precisa visitar cada um dos caminhos possíveis, e o número total de caminhos é exponencial, logo a complexidade vai ser do tipo ##O(c^n)##, onde a constante ##c## depende do grafo. Ou seja, você não quer usar esse algoritmo quando o ##n## é grande.

Multiplicação de Matrizes


Como melhorar então? Tem uma maneira calcular ##p(n)## mais rápido usando multiplicação de matrizes. É mais fácil mostrar como funciona por exemplos: no estado inicial, nós temos um lemming no nó A, então vamos montar um vetor correspondente: ##V=[1 \;0\; 0]^T##. Se multiplicarmos ##M## por ##V##, qual o resultado?

$$M V=\left[\begin{matrix}0 && 0 && 1 \\ 1 && 0 && 0 \\ 1 && 1 && 0\end{matrix}\right] \left[\begin{matrix}1\\0\\0\end{matrix}\right]=\left[\begin{matrix}0\\1\\1\end{matrix}\right]$$
Ou seja, um lemming que está em A vai parar em B ou C. Continuando:
$$M V=\left[\begin{matrix}0 && 0 && 1 \\ 1 && 0 && 0 \\ 1 && 1 && 0\end{matrix}\right] \left[\begin{matrix}0\\1\\1\end{matrix}\right]=\left[\begin{matrix}1\\0\\1\end{matrix}\right]$$
Dado um lemming em B ou C, o resultado é um lemming em A ou C. Se o vetor de entrada mostra quantos lemmings tem em cada nó, multiplicar pela matriz M vai mostrar onde eles podem estar depois de dar um passo.

Agora é só estender o raciocínio. Se multiplicar por M correponde a um passo, então multiplicar ##n## vezes por M é o equivalente a ##n## passos. E se você começou o processo com um vetor unitário, então a soma do elementos de V é o número de caminhos possíveis partindo do nó inicial!

Uma implementação possível é a abaixo:
count[graph_, pos_, size_] :=
Total[MatrixPower[graph, size] . UnitVector[Length[graph], pos]]
Conferindo:
graph := {{0, 0, 1}, {1, 0, 0}, {1, 1, 0}}
Print[Table[count[graph, 1, n], {n, 0, 6}]]

{1,2,2,3,4,5,7}
Bateu certinho novamente! E qual é a complexidade? Se você implementou a multiplicação e a exponenciação de matrizes do jeito mais simples, vai ser da ordem de ##O(k^3 n)##, que certamente é melhor que exponencial mas ainda não é boa o suficiente para usar na prática.

No entanto, você pode melhorar isso com implementações melhores! A multiplicação de matrizes pode usar o algoritmo de Strassen, e a exponenciação pode usar o método binário, aí o total fica ##O(k^{2.8} \log_2 n)##, bem melhor; e se a matriz for esparsa dá para ficar ainda mais rápido.

Função Geradora


Mas tem um método que supera o anterior, que é usar funções geradoras! Nós começamos tudo com um grafo orientado, e todo grafo orientado é equivalente a um autômato finito. Por sua vez, todo autômato finito é equivalente a uma gramática regular, então deve ter um jeito de descrever os caminhos do lemming usando uma gramática. Para o exemplo dado, a gramática fica assim:

$$\begin{align*}\alpha &\to A \;|\; A \beta \;|\; A \gamma \\ \beta &\to B \;|\;  B \gamma \\ \gamma &\to C \;|\; C \alpha\end{align*}$$
Todos os caminhos possíveis produzem strings que são aceitas por essa gramática, e a gramática não aceita nenhuma string que não seja um caminho possível.

O truque agora é usar combinatória analítica, que para o problema de enumerar strings de uma gramática é bem simples: basta trocar cada token por um ##z##, e cada OR por uma adição. A gramática vira um sistema de equações:

$$\begin{align*}\alpha &= z + z\beta +z \gamma \\ \beta &= z+z \gamma \\ \gamma &= z+z \alpha\end{align*}$$
Resolvendo para ##\alpha##, chegamos na função geradora:

$$\alpha=\frac{z + 2 z^2 + z^3}{1 - z^2 - z^3}$$
E para que serve a função geradora? Olhe só o que acontece quando abrimos a função em série de potências:

$$\frac{z + 2 z^2 + z^3}{1 - z^2 - z^3}=z+2z^2+2z^3+3z^4+4z^5+5z^6+7z^7+\dots$$
Ahá! Os coeficientes de ##z## são exatamente o número de caminhos no grafo! Para calcular ##p(6)##, basta olhar o coeficiente de ##z^7## (você precisa somar um porque a função geradora está contando o número de tokens na strings, e nós queremos o número de passos).

Podemos ir além agora. Quando a função geradora é uma função racional, você sempre consegue inverter e conseguir uma fórmula explícita para ##p(n)##:

$$p(n)=0.957\times 1.325^n+0.426\times 0.869^n\cos(1.469+ 2.438 n)$$
Essa fórmula é exata (ou seria, se eu não tivesse truncado os números para caber na tela). Note que o resultado sempre vai ser um inteiro, e que a parte oscilante da fórmula sempre vai ser menor que 0.4, então dá para simplificar mais ainda:

$$p(n)=\left\lfloor 0.5+0.957\times 1.325^n\right\rfloor$$
Basta pegar a parte exponencial e arredondar para o inteiro mais próximo. Podemos observar duas coisas aqui. Primeiro, agora nós temos um algoritmo onde o número de operações independe do valor de ##n##, então efetivamente esse algoritmo é ##O(1)##. Na prática você vai implementar usando bignum, aí não fica ##O(1)## de verdade; mas os outros algoritmos mostrados também vão ficar mais lentos na mesma proporção.

Segundo, nós provamos que aquele primeiro algoritmo era de fato ##O(c^n)##! Conseguimos até calcular quem é o tal do ##c##: é aproximadamente 1.325; ou, se você quiser o valor exato com radicais:

$$c=\frac{\sqrt[3]{9+\sqrt{69}}+\sqrt[3]{9-\sqrt{69}}}{\sqrt[3]{18}}$$
Para implementar esse método computacionalmente, é só reescrever aquele sistema de equações em forma matricial:

$$\left[\begin{matrix}\alpha\\ \beta \\ \gamma\end{matrix}\right] = z \left[\begin{matrix}0 && 1 && 1\\0 && 0 && 1\\ 1 &&0 && 0\end{matrix}\right] \left[\begin{matrix}\alpha\\ \beta \\ \gamma\end{matrix}\right]  + z\left[\begin{matrix}1\\ 1\\1\end{matrix}\right] $$
A matriz ali no meio é a transposta da matriz de adjacências! Agora já podemos implementar:
count[graph_, pos_, size_] := With[{n = Length[graph]},
SeriesCoefficient[LinearSolve[
IdentityMatrix[n] - z Transpose[graph],
ConstantArray[z, n]][[pos]],
{z, 0, size + 1}]]
Como esperado, a resposta é correta:
graph := {{0, 0, 1}, {1, 0, 0}, {1, 1, 0}}
Print[Table[count[graph, 1, n], {n, 0, 6}]]

{1,2,2,3,4,5,7}

Um Problema em Aberto


Nós chegamos em um algoritmo que é ##O(1)##, dá para otimizar mais? Não! Esse é um limite teórico.

Quer dizer, existem algoritmos que são ##O(0)##. Por exemplo, "crie um algoritmo que, dado uma circunferência e um diâmetro, calcule a razão entre os dois". Esse é algoritmo é ##O(0)## porque a resposta é sempre ##\pi##, independe do input. Mas, se a resposta não é constante, ##O(1)## é o melhor algoritmo possível. (A não ser que você crie um algoritmo que dê a resposta antes do input chegar. Nesse caso me ensine como faz, porque você inventou a viagem no tempo).

Mas existe uma pequena variação no problema dos lemmings que o torna muito, muito mais difícil. Suponha que agora o lemming não pode passar duas vezes pelo mesmo nó. A força bruta para essa variação ainda é ##O(c^n)##. Mas ninguém bolou um algoritmo que seja melhor que a força bruta nesse caso!

Quando o grafo é finito, você pode usar a força bruta para enumerar todas as soluções, já que eventualmente o grafo acaba. Mas se o grafo for infinito, aí danou-se. Para esse caso, tem gente procurando a solução desde a década de 50. Já são setenta anos procurando o algoritmo e ninguém achou nada para um grafo genérico, nem mesmo um assintótico! O melhor resultado até hoje é um de 2011 onde acharam o ##c## para um grafo específico, o lattice hexagonal. Para todos os outros grafos, o melhor que temos são heurísticas e aproximações numéricas.

Se você estiver sem nada o que fazer no próximo fim de semana, essa pode ser uma boa maneira de ocupar o tempo!

por Ricardo Bittencourt (noreply@blogger.com) em 19 de March de 2018 às 14:59

March 11, 2018

Filipe Saraiva

Procurando recomendações de “distros KDE”

Sou usuário e empacotador do Mageia desde o lançamento do fork, e não me levem a mal, para mim continua sendo uma distribuição de excelente qualidade para o seu propósito: comunitária, aberta para as mais diferentes contribuições e com ênfase na estabilidade. Mageia é das poucas distros com suporte há mais de 8 ambientes desktop (sem contar os gerenciadores de janelas leve), e com o lançamento da versão 6 passou a ter suporte ao AppImage, Fedora Copr, Open Build Service, dnf, e muito mais tecnologias que dão uma cara moderna para o projeto. Uso Mageia nos meus computadores pessoais e de trabalho e também nos computadores que meus alunos utilizam no laboratório.

Como desenvolvedor tanto do KDE quanto do Mageia, utilizo a versão instável da distro (chamada Cauldron) desde sempre. Ela me entrega as versões mais recentes da “pilha KDE” (KDE Plasma, Applications e Frameworks) e também do Qt. No geral funciona bem, mas volta e meia alguns software importantes deixam de funcionar ou ficam muito instáveis, prejudicando o desenvolvimento de algumas tarefas.

Antigamente isso não era um problema para mim – mesmo sendo estudante de mestrado ou doutorado, eu normalmente aguardava algum desenvolvedor corrigir os erros ou eu mesmo ia lá e metia a mão pra tentar solucionar. Essa é uma forma muito efetiva de contribuir com software livre.

Mas hoje em dia tenho pouca disposição para tanto. O trabalho como professor, somado às outras coisas a que me dedico, exaurem meu tempo para realizar esse tipo de tarefa.

Passar a utilizar o Mageia estável não é uma boa opção: o preço da estabilidade é ter um sistema com versões antigas dos software. Por exemplo, o Mageia 6 ainda utiliza o Plasma 5.8, enquanto o Cauldron tem o 5.12. Infelizmente, meu caso de uso mudou e as opções que o Mageia dispõe não casam muito bem com ele. Até propus um projeto não-oficial de construir os software mais recentes do KDE para a versão estável do Mageia, mas estou esperando um retorno do time de empacotadores da distro para ver o que eles acham (pois é, parágrafos cima eu disse que estava sem tempo e vejam só, estou propondo um novo projeto aqui :D).

Gostaria, portanto, de ouvir o pessoal sobre opções de distros que utilizam os software do KDE que atendam ao seguinte caso de uso:

  • Ser estável em seus componentes base (kernel, xorg, etc);
  • Ter a pilha KDE no modelo rolling release;
  • Ter opções relativamente amplas para software não Qt.

Na verdade eu já fiz uma pesquisa assim e há várias opções disponíveis. Por exemplo, o próprio KDE tem o projeto neon, que faz o caso de uso descrito acima tendo o Ubuntu como base. Há também o OpenSUSE com os repositórios Argon/Krypton, e mesmo distros específicas nesse modelo, como o KaOS e o Chakra. Inclusive isso vai render outro post sobre essas distros.

Minha principal dúvida é sobre a estabilidade e experiência de uso desses projetos. Se você usa algum deles, ou mesmo conhece outro não citado, coloque aí nos comentários para que embase minha decisão de migrar para outra distro (ou não).

por Filipe Saraiva em 11 de March de 2018 às 16:04

March 06, 2018

Vinta Software

Django REST Framework Read & Write Serializers

Django REST Framework (DRF) is a terrific tool for creating very flexible REST APIs. It has a lot of built-in features like pagination, search, filters, throttling, and many other things developers usually don't like to worry about. And it also lets you easily customize everything so you can make your API work the way you want.

There are many generic classes you can use to implement your API endpoints, some with more or fewer abstraction. Check Classy Django REST Framework to find the best class to use in each situation and understand how to override their behavior.

One family of classes DRF has is Serializers. They're used to convert the data sent in a HTTP request to a Django object and a Django object to a valid response data. It looks a lot like a Django Form, but it is also concerned in defining how the data will be returned to the user. There's also the ModelSerializer, which works like a ModelForm: it uses one of your models as a basis to create its fields and its validation.

Serializers use the same block of code for defining how the data will be interpreted when it comes from a request and how it will look like in a response. But handling both the input and output with the same code is not ideal. some cases, you probably will need more than one serializer for the same endpoint. That's why we made the drf-rw-serializers project: a set of generic views, viewsets and mixins that extend DRF to have different serializers for read and write operations.

Let's look at an example where this is the case. We'll build an API for a Cafe:

  • We'll need an endpoint that creates orders in the cafe;
  • Orders have a table number and a list of meals;
  • A meal has a title and a price;
  • This endpoint must have:
    • a GET method for listing the orders and;
    • a POST method to create a new order.
  • In the GET requests, I want to receive a list of all the orders and, for each order, I want to see every meal requested;
  • In the GET requests, I also want to have the total price of each order;
  • In the POST requests, since meals are pre-registered in the system, I want to send only the meals ids instead of the whole meal object.

Let's consider these models:

from django.db import models


class Order(models.Model):
    table_number = models.IntegerField()
    meals = models.ManyToManyField('meals')

    @property
    def total_price(self):
        qs = self.meals.all().aggregate(total_price=Sum('price'))
        return qs['total_price']


class Meal(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(decimal_places=2, max_digits=5)

For creating meals we need an OrderSerializer. The following example should be enough:

from rest_framework import serializers
from .models import Order


class OrderCreateSerializer(serializers.ModelSerializer):

    class Meta:
        model = Order
        fields = ('id', 'table_number', 'meals')

    def create(self, validated_data):
        meals = validated_data.pop('meals')
        instance = Order.objects.create(**validated_data)
        for meal in meals:
            instance.meals.add(meal)

        return instance

But when you try to reuse these serializers for the list operation you'll find out that they won't be enough. This happens because they don't contain the meals in the orders, only their ids. To fulfill a list operation we would need two other serializers:

from rest_framework import serializers
from .models import Order, OrderedMeal, Meal

class MealSerializer(serializers.ModelSerializer):

    class Meta:
        model = Meal
        fields = ('id', 'name', 'price')


class OrderListSerializer(serializers.ModelSerializer):
    ordered_meals = MealSerializer(many=True)

    class Meta:
        model = Order
        fields = ('id', 'table_number', 'meals', 'total_price')

In a case like this, we can't reuse the same serializer for doing both list and create, unless you override lower level methods like to_internal_value and to_representation, but this way you'd lose almost the whole abstraction ModelSerializer class gives you.

Another point for using two serializers instead of making workarounds to use only one is that the read logic and the write logic will probably evolve differently in a use case like this. So if the logic is so decoupled why to share lines of code?

This is one of the few cases where we shouldn't blindly follow the "Don't Repeat Yourself" philosophy. It's better to repeat lines of code that are known to have different concerns and will evolve separately. By doing this, we avoid messy workarounds to keep the code in one place.

However, DRF is not so friendly when you need to have multiple serializers in different methods of the same view. When you have write operations like create or update in your endpoints, you usually expect them to return the created/updated object. But not with the exact same data you sent: you want the data to look like the one users fetch from the API, with nested details and maybe some processed data.

Therefore, it's better to use one serializer to save the data sent in the request and another to return the created/updated resource representation.

To use DRF's wonderful generic API views you'd have to override some of their methods to implement this logic. For our Cafe API the implementation of the view would be something like this:

from rest_framework import generics
from .models import Orders
from .serializers import OrderCreateSerializer, OrderListSerializer


class OrderListCreateView(generics.ListCreateAPIView):
    queryset = Order.objects.all()
    serializer_class = OrderListSerializer

    def create(self, request, *args, **kwargs):
        write_serializer = OrderCreateSerializer(data=request.data)
        write_serializer.is_valid(raise_exception=True)
        instance = self.perform_create(write_serializer)

        read_serializer = OrderListSerializer(instance)

        return Response(read_serializer.data)

To avoid this, we at Vinta created generic classes similar to DRF ones to have separate input/output serializers. With our package, drf-rw-serializers, the OrderCreateView, would be like this:

from drf_rw_serializers import generics
from .models import Orders
from .serializers import OrderCreateSerializer, OrderListSerializer


class OrderListCreateView(generics.ListCreateAPIView):
    queryset = Order.objects.all()
    write_serializer_class = OrderCreateSerializer
    read_serializer_class = OrderListSerializer

Also if you need a different serializer for different methods you can override similar methods as the get_serializer_class from DRF generics.GenericAPIView. For instance, suppose that we want to use another serializer called OrderDetailSerializer when we're getting the response of create method. Our View would be like this:

from drf_rw_serializers import generics
from .models import Orders
from .serializers import (
    OrderCreateSerializer, OrderListSerializer, OrderDetailSerializer)


class OrderListCreateView(generics.ListCreateAPIView):
    queryset = Order.objects.all()
    write_serializer_class = OrderCreateSerializer

    def get_read_serializer_class(self):
        if self.request.method == 'POST':
            return OrderDetailSerializer

        return OrderListSerializer

Overriding get_read_serializer_class method is also useful if you want to check other condition like the user role or something in your app's context to choose the proper serializer for the read operations. As well as the method for retrieving the read operations serializer, we also made available a similar method for selecting the one for write operations (write_serializer_class) called get_write_serializer_class and it can also be overridden the same way we did previously with get_read_serializer_class.

drf-rw-serializers is open source, and its available on github and on PyPI. You can also read the full instruction of how to use it in its documentation.

We've been using an implementation like this for a while in Vinta's projects, but this is a brand new package so if you have any suggestions, questions or problems using it feel free to create issues on the github repository. Also, contributions are welcome.

Special thanks to Flávio Juvenal and Rebeca Sarai and Tulio Lages for reviewing this post.

por Hugo Bessa em 06 de March de 2018 às 14:00

March 05, 2018

Humberto Rocha

TLDR: Publicando no PyPI

Já se perguntou como publicar seu próprio pacote Python para instalá-lo com o pip? É menos complicado que parece, e qualquer pessoa pode fazer.

Basta acessar o Python Package Index (PyPI) e criar uma conta na qual irá registrar seus pacotes.

Em seguida crie um arquivo python chamado setup.py alterando os valores que forem necessários:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from setuptools import find_packages, setup

setup(
    name='meu-pacote-python',
    packages=find_packages(),
    version='1.0.0',
    description='Descrição curta do meu pacote',
    long_description='Longa descrição do meu pacote',
    author='Meu Nome',
    author_email='meu@email.com',
    url='https://github.com/usuario/meu-pacote-python',
    install_requires=['dependencia1', 'dependencia2'],
    license="MIT",
    keywords=['dev', 'web'],
    classifiers=[
        'Intended Audience :: Developers',
        'License :: OSI Approved :: MIT License',
        'Natural Language :: English',
        'Programming Language :: Python :: 2',
        'Programming Language :: Python :: 3',
    ],
)

O find_packages é uma ferramenta muito boa para encontrar automaticamente os arquivos que fazem parte do seu pacote qualquer dúvida veja sua documentação clicando aqui.

Os classifiers servem para ajudar usuários a encontrar seu pacote na busca e a lista completa deles pode ser vista neste link.

Empacotando

Antes de publicar o pacote é preciso empacotá-lo e a forma mais simples é fazendo o empacotamento do código fonte rodando:

$ python setup.py sdist

Este modelo de empacotamento requer que o pip rode uma etapa de montagem após a instalação

Para já empacotar o código montado rode:

$ python setup.py bdist_wheel

Este comando empacota a versão montada para a versão do python em que você rodou o comando, para montar para python 2 e 3 rode duas vezes o comando (uma para cada versão do python)

Com isso você terá tudo empacotado e pronto para a publicação na pasta dist/.

Publicando

Para publicar é preciso instalar o twine que é uma ferramenta para publicar pacotes no PyPI:

$ pip install twine

Em seguida basta rodar o seguinte comando:

$ twine upload --username usuario-no-pypi dist/meu-pacote-1.0.0.tar.gz dist/meu-pacote-1.0.0-py2-none-any.whl dist/meu-pacote-1.0.0-py3-none-any.whl

O comando vai pedir a senha e em seguida fazer o upload para o PyPI.

Bônus - Versionamento

Uma prática muito comum é versionar o código e adicionar algumas informações no __init__.py desta forma:

__author__ = 'Meu Nome'
__email__ = 'meu@email.com'
__version__ = '1.0.0'

Adicione este código no setup.py para usar os dados do __init__.py no seu pacote:

import os
import re

package = 'meu_pacote'
init_py = open(os.path.join(package, '__init__.py')).read()
version = re.search(
    "^__version__ = ['\"]([^'\"]+)['\"]", init_py, re.MULTILINE).group(1)
author = re.search(
    "^__author__ = ['\"]([^'\"]+)['\"]", init_py, re.MULTILINE).group(1)
email = re.search(
        "^__email__ = ['\"]([^'\"]+)['\"]", init_py, re.MULTILINE).group(1)
...
setup(
    ...
    version=version,
    author=author,
    author_email=email,
    ...
)

Bônus - README.md para rst

Quem publica código no github costuma escrever o arquivo readme em markdown, mas a sintaxe de documentação do PyPI é em reStructured Text.

Para converter sua documentação para a publicação você pode usar o pacote pandoc.

$ pip install pypandoc

O pandoc precisa estar instalado no sistema para ser usado com pypandoc. Veja a documentação

Adicione este código no seu setup.py para realizar a conversão:

try:
    import pypandoc
    readme = pypandoc.convert('README.md', 'rst')
except (IOError, ImportError):
    readme = ''

setup(
    ...
    long_description=readme,
    ...
)

É importante colocar a conversão dentro do readme dentro de um bloco try ... except para não demandar a instalação do pypandoc nos usuários que vão usar o seu pacote.

por Humberto Rocha em 05 de March de 2018 às 00:00

March 01, 2018

Thiago Avelino

Parabéns Mario Idival estou super ansioso em lhe ver contribuindo com o core da linguagem sei que…

Parabéns Mario Idival estou super ansioso em lhe ver contribuindo com o core da linguagem sei que você consegue chegar lá (basta querer), sucesso e parabéns pelo empenho, perseverança e foco. A dia que sempre do para quem quer contribuir com projeto Open Source: contribua com o que você usa no seu dia a dia, isso diminui a barreira de entrada e entendimento do eco sistema do projeto

por Avelino em 01 de March de 2018 às 22:13

História do Lisp, abra os olhos para programação funcional

Como começou minha paixão por parênteses (ops Lisp)?

Tenho uma formação na área de Matemática Aplicada (começa ai a loucura), Lisp foi uma das poucas linguagens de programação que me deparei dentro da academia (eu já programava em Perl profissionalmente e conhecia Python), quando vi aqueles parênteses de cara pensei isso não é para mim (isso é uma loucura de Matemática/Acadêmico que nunca saiu para o mercado de trabalho). Com o passar das aulas eu comecei achar confortável os parênteses (seres humanos acostuma muito rápido com tudo) e comecei achar estranho a forma de pensar para escrever a lógica do software (por exemplo (+ 1 2)). Lisp usa Notação polonesa como forma de expressão, isso foi complicado acostumar, no dia a dia usava uma linguagem de programação “normal” e dentro da academia funcional (sem saber que era funcional).

Com o tempo comecei pegar gosto pela forma de pensar (funcionalmente) e acabei entrando para um projeto (no mercado, não académico) que usava Common Lisp na sua implementação SBCL (Steel Bank Common Lisp, que é mantido até hoje). Nesse momento eu virei super fã da linguagem e com a forma de lidar com software (de verdade) em produção. A empresa trabalhava com dados estatístico do mercado de Pesquisa Cliníca e LISP foi tomado como linguagem por matemáticos, isso facilitou muito a comunicação entre o time de engenharia e acadêmicos.

Como tudo começou?

É um conjunto de linguagem de programação especificada pelo John McCarthy em 1955 saindo sua primeira versão em 1958 (durante um projeto de pesquisa em inteligência artificial) influenciado pelo seu aluno Alonzo Church. A motivação de McCarthy surgiu da idéia de desenvolver uma linguagem algébrica para processamento de listas para trabalho em IA (inteligência artificial). O seu nome vem de LISt Processing (a lista é a estrutura de dados fundamental desta linguagem). Tanto os dados como o programa são representados como listas, o que permite que a linguagem manipule o código fonte como qualquer outro tipo de dados.

Nascendo assim o Lisp 1, a versão que realmente foi distribuída por McCarthy e outros do MIT (Massachussets Institute of Tecnology) foi Lisp 1.5 (manual de programadores), assim chamada porque continha várias melhorias no interpretador Lisp 1 original, mas não foi uma grande reestruturação como planejado que fosse ser o Lisp 2.

Linha do tempo de linguagens que segue o dialeto Lisp

Vou começar apartir do Lisp 1.5 pois foi o primeiro a ser distribuido.

  • Lisp 1.5 (1955–1965)/Dialeto — Primeira implementação distribuída por McCarthy e outros do MIT
  • Maclisp (1965–1985)/Dialeto — Desenvolvido pelo MIT Project MAC (não relacionado Apple, nem ligado com McCarthy)
  • Interlisp (1970–1990)/Dialeto — Desenvolvido pela BBN Tecnologiapara PDP-10 rodando no sistema operacional Tenex, logo após adotado pela máquina Xerox Lisp o InterLisp-D
  • ZetaLisp (1975–1995)/Dialeto — denominada Lisp Machine — usado nas máquinas Lisp, descendente direto de Maclisp. Tendo como grande influência o Common Lisp
  • Scheme (1975 — mantido até hoje)/Dialeto — Diferente de Common Lisp, linguagem que usa dialeto Lisp
  • NIL (1975–1980)/Dialeto — Sucessor direto do Maclisp, com muitas influências de Scheme. Esta versão do Common Lisp estava disponível para plataformas de grande alcance e foi aceita por muitos como um padrão de fato até a publicação do ANSI Common Lisp (ANSI x3.226–1994).
  • Common Lisp (1980 — mantido até hoje)/Dialeto — aka Common Lisp the Language (a linguagem) — As tentativas e divergência entre ZetaLisp, Spice Lisp, nil, e S-1 Lisp para criar um dialeto sucessora para Maclisp. Common Lisp estava disponível para plataformas de grande alcance e foi aceita por muitos como padrão até a publicação do ANSI Common Lisp (ANSI X 3.226–1994)
  • CCL (1984 — mantido até hoje)/Implementação — baseado no dialeto Common Lisp, antiga MCL
  • T (1985 — mantido até hoje)/Dialeto — Derivado de Scheme escrito por Jonathan A. Rees, Kent M. Pitman e Norman, Adams da Yale University com experiencia de design de linguagem e implementação. Em 1987 foi publicado o livro The T Programming Language: A Dialect of LISP
  • Emacs Lisp (1985 — mantido até hoje)/Dialeto/Implementação — Usado como linguagem de script (configuração) do editor Emacs (mantido pelo projeto GNU)
  • AutoLISP (1985 — mantido até hoje)/Dialeto/Implementação — feito para AutoCAD, rodando nos produtos AutoCAD Map 3D, AutoCAD Architecture e AutoCAD Mechanical
  • OpenLisp (1985 — mantido até hoje)/Dialeto — desenvolvido por Christian Jullie escrito em C e Lisp que deu origem a implementação ISLISP
  • PicoLisp (1985 — mantido até hoje)/Dialeto — Open Sourcepara Linux e outros sistemas compatíveis com POSIX
  • EuLisp (1990–2015)/Dialeto — Escopo estático e dinâmico, introduzida para Indústria e Acamia Europeia
  • ISLISP (1990 — mantido até hoje)/Dialeto — feito para International Standard sobre licença de Dominio Público
  • newLISP (1990 — mantido até hoje)/Dialeto — Linguagem Open Source escrita por Lutz Mueller, ditribuída pela licença GPL (GNU General Public License) com fortes influencias de Common LispScheme
  • Racket (1990 — mantido até hoje)/Dialeto — Multi paradigma que veio da familía Scheme, um de seus objetivos de projeto é servir como uma plataforma para a criação de linguagens, design e implementação. Sua runtime usa JIT
  • GNU Guile (1990 — mantido até hoje)/Implementação — Usado para extensão de sistema para o Projeto GNU, baseado em Scheme
  • SBCL (1999 — mantido até hoje)/Implementação — baseado no dialeto Common Lisp com recurso de alta performance no compilador, suporte a unicode e threading. Nasceu como fork do Carnegie Mellon University Common Lisp por Andrew Carnegie e Andrew Mellon
  • Visual LISP (2000 — mantido até hoje)/Dialeto/Implementação — Antigo AutoLisp após ser comprado pela Autodesk
  • Clojure (2005 — mantido até hoje)/Dialeto/Implementação — começou sendo baseado Common Lisp rodando na JVM (Java Virtual Machine), trazendo retro compatibilidade com todas linguagens que roda na JVM, é possível importar classes Java por exemplo
  • Arc (2005 — mantido até hoje)/Dialeto/Implementação — desenvolvido por Paul Graham e Robert Morris escrito usando Racket
  • LFE (2005 — mantido até hoje)/Dialeto/Implementação — Lisp Flavored Erlang, implementado em Erlang
  • ACL2 (2005 — mantido até hoje)/Dialeto/Implementação — Criado por Matt Kaufmann e J Strother Moore dentro da University of Texas at Austin
  • Hy (2013 — mantido até hoje)/Dialeto/Implementação — apelido para Hylang, implementado em Python. Nasceu dentro da PyCon 2013 escrito por Paul Tagliamonte
  • Rum (2017 — mantido até hoje)/Dialeto/Implementação — implementado em Go, projeto extremamente novo, veja o github

Veja a timeline visual

Não para por aqui, existe muitas outras derivações…

É isso mesmo, Lisp é uma linguagem de programação que tem vários dialetos e implementações, com a ANSI Common Lisp itilizada como o dialeto mais utilizado.

Existe outra linguagem (mais usada que Lisp) o SQL ANSI (American National Standards Institute): — PostgreSQL — MySQL — Oracle — Microsoft SQL — e outros

Todos tem como dialeto o SQL ANSI mas evoluiu a partir do default.

Por onde começar estudar Lisp?

Vamos supor que você gostou da loucura que é as diversas implementações e você quer estudar essa linguagem, por onde você começa?

Qual interpretador usar? Eu particularmente gosto do SBCL (particularmente é o que mais tenho usado para desenvolvimento), em produção uso CCL, se você trabalha com alguma tecnologia que roda em JVM vai para Clojure.

Segue alguns links:

Domain Specific Languages in Lisp — The Killer-App for Domain Specific Language https://t.co/jfHIEoWMsE #clisp #lisp #dsl #functional
— Avelino (@avelino0) January 6, 2018

Originally published at avelino.xxx.

por Avelino em 01 de March de 2018 às 22:03

February 26, 2018

Filipe Saraiva

Papo Livre sobre KDE

Papo Livre é um podcast que vem movimentando a cena do software livre no país. Tocado pelos amigos Antonio Terceiro, Paulo Santana e Thiago Mendonça, o projeto já tem quase 1 ano e trouxe para os ouvintes muita informação e entrevistas com brasileiros criadores ou participantes dos mais diferentes projetos de software livre.

Semanas atrás estive no programa concedendo entrevista sobre o KDE e falei bastante: comentei sobre a história do KDE, como o projeto passa de um ambiente desktop para uma comunidade com interesse em executar software livre nos mais diferentes dispositivos, ciclos de lançamento, como contribuir, como a comunidade se estrutura no Brasil, e muito mais. Também falei em temas mais pessoais como de que forma comecei no KDE, o que faço por lá, entre outras coisas.

A recepção do episódio tem sido muito boa, portanto dê uma escutada e comente abaixo o que achou – ficarei muito grato com o feedback de vocês.

E não deixem de conferir os demais episódios do Papo Livre.

por Filipe Saraiva em 26 de February de 2018 às 13:55

Python Help

Preservando a ordem de sequências ao remover duplicatas

Imagine que você tenha uma lista com as URLs extraídas de uma página web e queira eliminar as duplicatas da mesma.

Transformar a lista em um conjunto (set) talvez seja a forma mais comum de se fazer isso. Tipo assim:

>>> urls = [
    'http://api.example.com/b',
    'http://api.example.com/a',
    'http://api.example.com/c',
    'http://api.example.com/b'
]
>>> set(urls)
{'http://api.example.com/a',
 'http://api.example.com/b',
 'http://api.example.com/c'}

Porém, observe que perdemos a ordem original dos elementos. Esse é um efeito colateral indesejado da eliminação de duplicatas através da transformação em um conjunto.

Um jeito de preservar a ordem dos elementos após a remoção das duplicatas é utilizar este macete com collections.OrderedDict:

>>> from colllections import OrderedDict
>>> OrderedDict.fromkeys(urls).keys()
['http://api.example.com/b',
 'http://api.example.com/a',
 'http://api.example.com/c']

Legal né? Agora vamos entender o que o código acima fez.

Antes de mais nada, é preciso saber que OrderedDict é uma estrutura de dados muito similar a um dicionário. A grande diferença é que o OrderedDict guarda internamente a ordem de inserção dos elementos. Assim, quando iteramos sobre um objeto desse tipo, ele irá retornar seus elementos na ordem em que foram inseridos.

Agora vamos quebrar as operações em partes para entender melhor o que aconteceu:

>>> odict = OrderedDict.fromkeys(urls)

O método fromkeys() cria um dicionário usando como chaves os valores passados como primeiro parâmetro e como valor o que for passado como segundo parâmetro (ou None, caso não passemos nada).

Como resultado, temos:

>>> odict
OrderedDict([('http://api.example.com/b', None),
('http://api.example.com/a', None),
('http://api.example.com/c', None)])

Agora que temos um dicionário com as chaves mantidas em ordem de inserção, podemos chamar o método keys() para obter somente as chaves que, neste caso, são as nossas URLs:

>>> odict.keys()
['http://api.example.com/b',
'http://api.example.com/a',
'http://api.example.com/c']

Obs.: em Python 3, o método keys() retorna uma view ao invés de uma lista. Esse tipo de objeto suporta iteração e teste de pertinência, assim como a lista. Caso você realmente precise de uma lista, basta construir uma usando o resultado de keys():

>>> list(odict.keys())

A eliminação de duplicatas é só uma consequência do design de dicionários, já que a unicidade das chaves é uma das propriedades fundamentais dessa estrutura de dados.

Caso queira entender melhor os princípios por trás de um dicionário, leia sobre tabelas hash: https://www.ime.usp.br/~pf/estruturas-de-dados/aulas/st-hash.html

por Valdir Stumm Jr em 26 de February de 2018 às 01:31

February 17, 2018

Humberto Rocha

TLDR: Executando comandos em multiplos hosts com Fabric

Me perguntaram como realizar uma conexão ssh em um servidor para rodar o comando df -h que mostra o uso de disco da máquina. Logo depois descobri que a dúvida se estendia a como fazer isso em vários servidores.

A resposta me veio rápido na mente, e junto com ela a ideia de documentar estas coisas, o que me trouxe ao começo desta série de postagens curtas carinhosamente batizada de TLDR, que é a abreviação de Too Long Didn`t Read ou, em português, Muito longo, não li.

Pois bem, para automatizar tarefas repetitivas tal como o tão falado processo de deploy (implantação de software) ou a execução de um único comando em múltiplos servidores existe uma ferramenta muito legal e prática chamada Fabric que utilizaremos para automatizar a questão levantada no início da postagem.

Para instalar o Fabric em python 2 rode:

$ pip install Fabric

Ou para python 3:

$ pip install Fabric3

Agora vamos escrever o código para rodar o df -h nos servidores e salvar em um arquivo chamado fabfile.py:

from fabric.api import env, run

env.hosts = ['user1@server1', 'user2@server2']


def disk_usage():
    run('df -h')

O arquivo deve se chamar fabfile.py pois o Fabric busca automaticamente por este arquivo quando rodamos o comando

O Fabric faz uma conexão ssh com os servidores, portanto tenha uma chave ssh configurada em sua máquina e liberada no allowed_hosts dos usuários dos servidores de destino

Com o arquivo pronto é só rodar o comando definido no aquivo:

$ fab disk_usage
[user1@server1] Executing task 'disk_usage'
[user1@server1] run: df -h
[user1@server1] out: Filesystem      Size  Used Avail Use% Mounted on
[user1@server1] out: /dev/root        20G  2.2G   17G  12% /
[user1@server1] out: devtmpfs        488M     0  488M   0% /dev
[user1@server1] out: tmpfs           490M     0  490M   0% /dev/shm
[user1@server1] out: tmpfs           490M   51M  440M  11% /run
[user1@server1] out: tmpfs           5.0M     0  5.0M   0% /run/lock
[user1@server1] out: tmpfs           490M     0  490M   0% /sys/fs/cgroup
[user1@server1] out: tmpfs            98M     0   98M   0% /run/user/0
[user1@server1] out:

[user2@server2] Executing task 'disk_usage'
[user2@server2] run: df -h
[user2@server2] out: Filesystem      Size  Used Avail Use% Mounted on
[user2@server2] out: /dev/root        20G  3.9G   15G  22% /
[user2@server2] out: devtmpfs        488M  4.0K  488M   1% /dev
[user2@server2] out: none            4.0K     0  4.0K   0% /sys/fs/cgroup
[user2@server2] out: none             98M  352K   98M   1% /run
[user2@server2] out: none            5.0M     0  5.0M   0% /run/lock
[user2@server2] out: none            490M     0  490M   0% /run/shm
[user2@server2] out: none            100M     0  100M   0% /run/user
[user2@server2] out:


Done.

O Fabric roda a tarefa em sequência na lista de hosts informada. Caso queira rodar os comandos em paralelo é só passar a flag -P para o comando:

$ fab disk_usage -P

Lembrando que ao rodar a tarefa em paralelo você perde o controle da ordem de execução da sua lista de servidores

Bônus

Dica do @luizirber no twitter.

O Fabric possuí um padrão de comando muito legal que nos permite executar o exemplo acima totalmente da linha de comando que é:

$ fab [options] -- [shell command]

O comando ficaria desta forma:

$ fab -H 'user1@server1, user2@server2' -- df -h

por Humberto Rocha em 17 de February de 2018 às 00:00

February 16, 2018

PythonClub

Django Rest Framework - #3 Class Based Views

Este post é continuação do post Django Rest Framework Requests & Responses.

Finalmente chegamos as views baseadas em classes. A grande vantagem é que com poucas linhas de código já temos nossa API pronta.

Veja como fica a views.py:

from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from core.models import Person
from core.serializers import PersonSerializer


class PersonList(APIView):
    """
    List all persons, or create a new person.
    """

    def get(self, request, format=None):
        persons = Person.objects.all()
        serializer = PersonSerializer(persons, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = PersonSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class PersonDetail(APIView):
    """
    Retrieve, update or delete a person instance.
    """

    def get_object(self, pk):
        try:
            return Person.objects.get(pk=pk)
        except Person.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        person = self.get_object(pk)
        serializer = PersonSerializer(person)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        person = self.get_object(pk)
        serializer = PersonSerializer(person, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        person = self.get_object(pk)
        person.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

E urls.py:

urlpatterns = [
    path('persons/', views.PersonList.as_view()),
    path('persons/<int:pk>/', views.PersonDetail.as_view()),
]

Usando Mixins

Repare que no exemplo anterior tivemos que definir os métodos get(), post(), put() e delete(). Podemos reduzir ainda mais esse código com o uso de mixins.

from rest_framework import mixins
from rest_framework import generics
from core.models import Person
from core.serializers import PersonSerializer


class PersonList(mixins.ListModelMixin,
                 mixins.CreateModelMixin,
                 generics.GenericAPIView):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class PersonDetail(mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   generics.GenericAPIView):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

Usando generic class-based views

E para finalizar usamos ListCreateAPIView e RetrieveUpdateDestroyAPIView que já tem todos os métodos embutidos.

from rest_framework import generics
from core.models import Person
from core.serializers import PersonSerializer


class PersonList(generics.ListCreateAPIView):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer


class PersonDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer

Versão final de views.py.

Abraços.

por Regis da Silva em 16 de February de 2018 às 01:00

February 15, 2018

PythonClub

Django Rest Framework - #2 Requests and Responses

Este post é continuação do post Django Rest Framework Serialization.

O uso de requests e responses torna nossa api mais flexível. A funcionalidade principal do objeto Request é o atributo request.data, que é semelhante ao request.POST, mas é mais útil para trabalhar com APIs.

Objeto Response

Introduzimos aqui um objeto Response, que é um tipo de TemplateResponse que leva conteúdo não renderizado e usa a negociação de conteúdo para determinar o tipo de conteúdo correto para retornar ao cliente.

return Response(data) # Renderiza para o tipo de conteúdo conforme solicitado pelo cliente.

Repare também no uso de status code pré definidos, exemplo: status.HTTP_400_BAD_REQUEST.

E usamos o decorador @api_view para trabalhar com funções. Ou APIView para classes.

Nosso código ficou assim:

# views.py
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from core.models import Person
from core.serializers import PersonSerializer


@api_view(['GET', 'POST'])
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 Response(serializer.data)

    elif request.method == 'POST':
        serializer = PersonSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'PUT', 'DELETE'])
def person_detail(request, pk):
    """
    Retrieve, update or delete a person instance.
    """
    try:
        person = Person.objects.get(pk=pk)
    except Person.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

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

    elif request.method == 'PUT':
        serializer = PersonSerializer(person, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        person.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Veja no GitHub.

Usando sufixo opcional

Em core/urls.py acrescente

from rest_framework.urlpatterns import format_suffix_patterns

...

urlpatterns = format_suffix_patterns(urlpatterns)

E em views.py acrescente format=None como parâmetro das funções a seguir:

def person_list(request, format=None):

def person_detail(request, pk, format=None):

Com isso você pode chamar a api da seguinte forma:

http http://127.0.0.1:8000/persons.json # ou
http http://127.0.0.1:8000/persons.api

Até a próxima.

por Regis da Silva em 15 de February de 2018 às 01:00

February 13, 2018

Humberto Rocha

Separando o Frontend do Backend com Angular e Django

No mundo do desenvolvimento web as coisas vem evoluindo muito rápido e crescendo em complexidade em um ritmo intenso. Aquele modelo antigo de criação de sites onde tinamos o webmaster que dominava o html e logava no servidor para atualiza uma página é algo cada vez mais raro de se ver. Até para criar sites estáticos estamos utilizando ferramentas para agilizar nosso trabalho.

Nos últimos anos tivemos um crescimento expressivo dos frameworks javascript e uma grande evolução do frontend como um todo, e estas novas tecnologias possuem ferramentas e fluxos de desenvolvimento próprios, o que vem tornando mais oneroso para um sistema só entregar o backend e o frontend.

Ao mesmo tempo vem se popularizando e ficando mais interessante o uso de API`s e a separação do frontend do backend em diferentes projetos, o que trás a proposta de dar mais autonomia aos times de desenvolvimento além de tornar a aplicação mais escalável.

Contudo, ao deparar com esta realidade muita gente (eu incluso em minha primeira tentativa de implementação) fica perdida em como botar em prática esta forma de se implementar sistemas web.

Nesta postagem veremos um exemplo simplificado de como botar em prática esta arquitetura. Criaremos uma aplicação de lista de compras onde poderemos adicionar visualizar e excluir itens da lista. Para isso utilizaremos Django 2 para o backend e Angular 5 para o frontend.

Separando as Responsabilidades

Para começar vamos falar um pouco sobre esta separação de responsabilidades dentro do projeto.

Existem duas grandes responsabilidades dentro de uma aplicação web interativa. A gestão e a visualização dos dados.

Backend

Em uma arquitetura web de cliente e servidor, nos referimos ao backend como a camada de acesso aos dados que é processada totalmente pelo servidor responsável pelo serviço. É dele a responsabilidade de acessar, processar, armazenar e controlar o acesso os dados requisitados pelo cliente. Seu modelo de implementação mais famoso e encontrado na internet são as API`s REST.

Frontend

No modelo citado acima, o frontend é responsável pela camada de apresentação dos dados e seu processamento acontece em sua maioria no cliente, mas em alguns casos parte deste processamento pode ser feito pelo servidor.

Implementando o backend

Definidas as responsabilidades vamos implementar nossa aplicação, partindo do backend.

Começamos com a instalação do django seguido da criação do projeto:

$ pip install django
$ django-admin startproject backend
$ cd backend
$ python manage.py migrate
$ python manage.py runserver

Ao iniciar o navegador em localhost:8000 veremos a nova tela de sucesso de criação de aplicação do Django 2 mostrando que a inicialização da sua nova aplicação foi um sucesso (muito bonita a nova tela né?!).

novo projeto

O próximo passo é instalar o Django Rest Framework, para montar a api do nosso backend:

$ pip install djangorestframework

Em seguida editamos o settings.py para habilitá-lo:

INSTALLED_APPS = (
    ...
    'rest_framework',
)

No urls.py registramos as urls de autenticação para acessar a interface de navegação de api

urlpatterns = [
    ...
    path('api-auth/', include('rest_framework.urls')),
]

Agora podemos visualizar a tela de login acessando localhost:8000/api-auth/login/

rest login

Com isso, daremos inicio a nossa aplicação de lista de compras:

python manage.py startapp shopping

A aplicação será bem direta ao ponto com um único model que é quem representa a lista de compras no banco de dados, um serializer que transforma os dados de objeto para json e vice versa, além de e validar os dados da requisição e uma view que recebe, encaminha e responde a estas requisições:

# settings .py

INSTALLED_APPS = (
    ...
    'shopping',
)

# models.py

from django.db import models


class ShoppingItem(models.Model):

    name = models.CharField(max_length=60)
    quantity = models.PositiveSmallIntegerField()
    checked = models.BooleanField(default=False)

# serializers.py

from rest_framework import serializers

from .models import ShoppingItem


class ShoppingItemSerializer(serializers.ModelSerializer):

    class Meta:
        model = ShoppingItem
        fields = '__all__'

# views.py

from rest_framework import viewsets

from .models import ShoppingItem
from .serializers import ShoppingItemSerializer


class ShoppingItemViewSet(viewsets.ModelViewSet):

    serializer_class = ShoppingItemSerializer
    queryset = ShoppingItem.objects.all()

# urls.py

from django.urls import include, path
from rest_framework import routers

from shopping.views import ShoppingItemViewSet

router = routers.DefaultRouter()
router.register('shopping-item', ShoppingItemViewSet,
                base_name='shopping-item')

urlpatterns = [
    ...
    path('', include(router.urls)),
]

Para finalizar criamos o arquivo de migração do banco de dados seguido da migração do mesmo:

$ python manage.py makemigrations
$ python manage.py migrate

E voilá, nosso backend está funcionando:

shopping list

Implementando o frontend

Para iniciar a implementação do frontend vamos instalar a ferramenta de linha de comando do Angular e criar um novo projeto:

$ npm install -g @angular/cli
$ ng new frontend
$ cd frontend
$ ng serve

Se tudo tiver corrido como esperado ao acessar localhost:4200 veremos a página inicial de um novo projeto Angular.

home angular

Agora criaremos um service para acessar nossa api e uma view para listar os itens da nossa lista de compras:

// shopping-item.interface.ts

export interface ShoppingItem {
  id: number;
  name: string;
  quantity: number;
  checked: boolean;
}

// api.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class ApiService {

  private apiRoot = 'http://localhost:8000/';

  constructor(private http: HttpClient) { }


  getShoppingItems() {
    return this.http.get(this.apiRoot.concat('shopping-item/'));
  }
}

//  app.component.ts

import { Component, OnInit } from '@angular/core';

import { ApiService } from './api.service';
import { ShoppingItem } from './shopping-item.interface';

@Component({
  selector: 'app-root',
  template: `
  <div style="text-align:center">
    <h1>
      Lista de compras
    </h1>
  </div>
  <ul>
    <li *ngFor="let item of items">
      <h2>{{ item.name }}</h2>
    </li>
  </ul>
  {{ error?.message }}
  `
})
export class AppComponent implements OnInit {

  items: ShoppingItem[];
  error: any;

  constructor(private api: ApiService) { }

  ngOnInit() {
    this.api.getShoppingItems().subscribe(
      (items: ShoppingItem[]) => this.items = items,
      (error: any) => this.error = error
    );
  }
}

// app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { ApiService } from './api.service';
import { AppComponent } from './app.component';


@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
  ],
  providers: [
    ApiService,
  ],
  bootstrap: [
    AppComponent,
  ],
})
export class AppModule { }

Acessando localhost:4200 veremos nossa belíssima lista de comp...

ops

wtf

Algo está errado com nossa aplicação, vamos dar uma olhada no console do browser:

cors error

Ding! ding! ding! parece que encontramos o problema!

xeroque holmes

Cross-Origin Resource Sharing (CORS)

Por questões de segurança navegadores implementam a política de mesma origem, que impede que scripts executem requisições HTTP de origem cruzada, ou seja, requisições que possuam protocolo, domínio ou porta diferentes de seu host de origem.

Para habilitar requisições de origem cruzada de forma segura foi criado o Mecanismo de Compartilhamento de Recursos entre Origens (CORS) que serve para permitir a comunicação entre um frontend e backend em domínios distintos, e também para requisições de fontes, imagens dentre outros recursos armazenados em uma CDN.

O mecanismo opera adicionando novos cabeçalhos HTTP que permitem que o servidor descreva o conjunto de origens que podem ler a informação através do navegador. Além de requerer que os navegadores "pré-enviem" requisições que podem causar efeitos colaterais ao servidor (como POST, PUT e DELETE) com uma requisição OPTIONS que depois de aprovada libera o envio da requisição verdadeira.

O navegador (por consequência nosso frontend também) já está pronto para lidar com este mecanismo, nos resta agora implementá-lo em no backend.

$ pip install django-cors-headers

Após instalado, basta adicionar as configurações no settings.py:

INSTALLED_APPS = (
    ...
    'corsheaders',
)

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]

CORS_ORIGIN_WHITELIST = (
    'http://localhost:4200',
)

Lembre-se de colocar o CorsMiddleware o mais a cima o possível na lista de middlewares para que ele consiga adicionar o cabeçalho na requisição antes que ela seja tratada pelo backend.

Em CORS_ORIGIN_WHITELIST colocamos somente a origem que queremos liberar o acesso que no nosso caso é localhost:4200, mas em ambiente de produção seria algo como https://api.backend.com.

Feito isso agora podemos ver que a comunicação entre frontend e backend está funcionando \o/.

funcionou

Agora para finalizar vamos adicionar um pouco de interatividade em nossa lista de compras:

// api.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class ApiService {

  private apiRoot = 'http://localhost:8000/';

  constructor(private http: HttpClient) { }


  getShoppingItems() {
    return this.http.get(this.apiRoot.concat('shopping-item/'));
  }

  createShoppingItem(name: string, quantity: number) {
    return this.http.post(
      this.apiRoot.concat('shopping-item/'),
      { name, quantity }
    );
  }

  deleteShoppingItem(id: number) {
    return this.http.delete(this.apiRoot.concat(`shopping-item/${id}/`));
  }
}

//  app.component.ts

import { Component, OnInit } from '@angular/core';

import { ApiService } from './api.service';
import { ShoppingItem } from './shopping-item.interface';

@Component({
  selector: 'app-root',
  template: `
  <div style="text-align:center">
    <h1>
      Lista de compras
    </h1>
  </div>
  <ul>
    <li *ngFor="let item of items">
      <h2>{{ item.quantity }}x {{ item.name }}
      <button (click)="delete(item.id)">x</button></h2>
    </li>
  </ul>

  <input #itemQuantity type='text' placeholder='Qtd'>
  <input #itemName type='text' placeholder='Name'>
  <button (click)="add(itemName.value, itemQuantity.value)">Add</button>
  {{ error?.message }}
  `
})
export class AppComponent implements OnInit {

  items: ShoppingItem[];
  error: any;

  constructor(private api: ApiService) { }

  ngOnInit() {
    this.api.getShoppingItems().subscribe(
      (items: ShoppingItem[]) => this.items = items,
      (error: any) => this.error = error
    );
  }

  add(itemName: string, itemQuantity: number) {
    this.api.createShoppingItem(itemName, itemQuantity).subscribe(
      (item: ShoppingItem) => this.items.push(item)
    );
  }

  delete(id: number) {
    this.api.deleteShoppingItem(id).subscribe(
      (success: any) => this.items.splice(
        this.items.findIndex(item => item.id === id))
    );
  }
}

E agora sim, temos a lista de compras completa e funcionando:

lista de compras

Conclusão

Separar o frontend do backend em projetos distintos gera um pouco mais de trabalho em relação a manter tudo em um único projeto monolítico. Porém, depois de acostumar com as particularidades desta separação é que começam a surgir seus frutos como poder dar mais flexibilidade e liberdade aos times de desenvolvimento para utilizarem suas ferramentas e fluxos de desenvolvimento da forma que bem entenderem, além de retirar a carga de processamento de template de seu servidor de backend deixando-o focado na gestão e processamento dos seus dados e, talvez uma das maiores vantagens caso você tenha uma empresa, a possibilidade de poder contratar profissionais especializados em frontend e em backend para evoluir seu produto sem demandar de todos um conhecimento de fullstack.

por Humberto Rocha em 13 de February de 2018 às 00:00

January 19, 2018

Ricbit

A Intuição do Knuth

Às vezes eu me pergunto se as pessoas da minha área têm noção de quão sortudos nós somos. Os físicos adorariam viajar no tempo para conversar com o Newton, os matemáticos adorariam conversar com o Euclides, os biólogos adorariam conversar com o Darwin. Mas nós podemos conversar com o Knuth!


Nós temos a sorte de viver no mesmo período de tempo que o criador da análise de algoritmos, que é uma das bases da Ciência da Computação. Se você gosta do assunto, vale a pena juntar uns trocos e viajar até a Califórnia para assistir a uma das palestras dele (dica: todo fim de ano, inspirado nas árvores de Natal, ele faz uma palestra de estrutura de dados, falando sobre árvores; elas também estão online se você não tiver como ver ao vivo).

Eu fiz a peregrinação em 2011, quando consegui assistir a uma das palestras dele. Aproveitei para ir todo contente pegar minha recompensa por ter achado um erro no Art of Computer Programming, mas ele, marotamente, me disse que aquilo que eu achei não era um erro, era uma pegadinha, e eu caí! (Mas eu não vou falar qual a pegadinha, vá na página 492 do TAOCP volume 4A, primeira edição, e confira você mesmo :)

Eu e Knuth, o trollzinho

Nesse dia perguntaram que opinião ele tinha sobre o problema mais difícil da nossa geração, P=NP. A intuição dele é que provalmente é verdade, mas ele acredita que se acharmos a demonstração, ela vai ser não-construtiva. O que isso significa? O que é uma demonstração não-construtiva?

Demonstrações construtivas e não-construtivas


Em análise de algoritmos, as demonstrações construtivas são as mais comuns. Por exemplo, digamos que eu quero provar que é possível calcular x elevado a y em tempo O(y). Isso é fácil, basta construir um algoritmo assim:
E se eu quiser provar que esse mesmo problema pode ser resolvido em tempo O(log y)? Novamente, tudo que eu preciso fazer é exibir um algoritmo que implemente isso:

(Nesse caso eu também precisaria provar que esse algoritmo é de fato O(log y), já não é óbvio por inspeção). Nos dois casos temos exemplos de demonstrações construtivas: se eu quero provar uma propriedade P, basta exibir um algoritmo que tenha essa propriedade P.

As demonstrações não-construtivas são diferentes. Nelas, eu posso provar a propriedade P sem mostrar o algoritmo, através de alguma propriedade matemática do modelo.

Por exemplo, imagine que eu tenho uma lista ordenada de números. Se eu fizer uma busca binária, posso achar a posição de um número dado com O(log n) comparações. Mas é possível criar um algoritmo mais rápido que isso? Eu digo que não é possível, e para isso vou fazer uma prova não-construtiva de que esse é o mínimo que um algoritmo de busca precisa para funcionar.

A Teoria de Shannon aplicada à busca binária


Para isso eu vou usar a teoria da informação de Shannon. Essa teoria é surpreendentemente intuitiva, e se baseia no conceito de surpresa. Se eu te falar que o céu ficou escuro às 19h, você não vai achar nada de mais, nessa hora o Sol está se pondo, então é natural que o céu fique escuro. Mas e se eu falar que o céu ficou escuro às 10 da manhã? Foi uma tempestade? Um eclipse? A nave do Independence Day?

Intuitivamente, quanto mais surpresos nós ficamos com uma sentença, mais informação ela tem. O Shannon definiu então a quantidade de informação como sendo uma função monotônica da probabilidade do evento acontecer:

I(m)=\log\left(\frac{1}{p(m)}\right)

Se o evento é raro, tem bastante informação; se o evento é comum, tem pouca informação. A base do logaritmo fornece a unidade de medida, se a base for 2, então a informação é medida em bits.

E quanta informação nós ganhamos com uma comparação? Se a chance de dar verdadeiro ou falso for a mesma, então a chance é p(m)=1/2, logo a informação é I(m)=1. Você ganha exatamente um bit de informação com uma comparação.

Qual o resultado do nosso algoritmo de busca? O resultado é um índice, se nós temos n elementos no vetor, então a resposta é um índice que varia de 0 a n-1. Logo, a probabilidade de você escolher o índice certo ao acaso é p(m)=1/n, já que a escolha é uniforme.

Quanta informação tem essa escolha, então? Fazendo a conta:


Se você precisa de log n bits para descrever a resposta, e você ganha só 1 bit por comparação, então não tem como um algoritmo rodar em menos que O(log n): a informação tem que vir de algum lugar! Com isso, nós mostramos que qualquer algoritmo precisa rodar no mínimo em tempo O(log n), e sem precisar mostrar o algoritmo em si. Essa é uma demonstração não-construtiva.

Pressinto a pergunta: "mas RicBit, e a busca com hash table, ela não é O(1)?". Sim, ela é! Mas ela não usa comparações, e a nossa análise foi exclusivamente para métodos baseados em comparações. Com um acesso a uma hash você pode ganhar mais que 1 bit de informação por operação.

O limite da ordenação


Um outro exemplo é achar o limite dos algoritmos de ordenação. Suponha que eu tenho um vetor com elementos bagunçados e quero ordená-los usando comparações. Eu sei que cada comparação ganha 1 bit de informação, então só preciso saber quanta informação tem na saída.

Qual o resultado do algoritmo? Um vetor ordenado. Mas os valores do vetor em si são irrelevantes, o que importa mesmo é saber a ordem relativa entre eles. Essa ordem relativa pode ser expressa como uma permutação dos itens originais.

Quantas permutações existem? Se o vetor tem tamanho n, então existem n! permutações, logo a probabilidade é 1/n!. Fazendo as contas:

\begin{align*}I(m)&=\log\left(\frac{1}{p(m)}\right)=\log\left(1/\frac{1}{n!}\right)=\log \left(n!\right)\\&\sim\log\left(n^n e^{-n}\sqrt{2\pi n}\right)\\&\sim n\log n-n-\frac{1}{2}\log\left(2\pi n\right)\\&\sim O(n \log n)\end{align*}

Primeiro você usa a aproximação de Stirling, depois joga fora todos os termos assintoticamentes menores que o dominante. O resultado é que nós provamos que nenhuma ordenação pode ser melhor que O(n log n), sem precisar mostrar nenhum algoritmo!

Novamente, esse resultado só vale para ordenações baseadas em comparações. Sem usar comparações, você tem métodos como radix sort e ábaco sort que são melhores que O(n log n).

A análise por quantidade de informação


Esse método de análise da quantidade de informação pode ser utilizado em qualquer algoritmo, desde que você note um detalhe muito importante: o método acha um limite inferior para a complexidade, mas não prova que esse algoritmo existe! Tudo que conseguimos provar como ele é que, se o algoritmo existir, então ele não pode ser melhor que o limite achado.

por Ricardo Bittencourt (noreply@blogger.com) em 19 de January de 2018 às 14:36

O dia que o Knuth ganhou meu cheque

No último dia 10, o Donald Knuth completou 80 anos de vida. Para comemorar, ele fez uma festinha na remota e gelada cidadezinha de Piteå, no norte da Suécia, e eu tive a sorte de poder participar. A festa foi em formato de simpósio de matemática: ao invés de presentes, cada amigo apresentou uma palestra sobre um tema relacionado ao trabalho do Knuth. (E os amigos dele são os caras mais famosos da computação, estavam lá o Sedgewick, o Karp, o Tarjan, entre outros).



Eu também fiz minha contribuição, presenteei o Knuth com vários artigos contendo idéias para futuras revisões dos livros dele. Mas em um dos artigos aconteceu uma coisa engraçada: o Knuth notou que uma das minhas contas podia ser simplificada, e como agradecimento pela sugestão eu tive a oportunidade de inverter expectativas, e dar um cheque para o Knuth :D


A Soma dos Quadrados


O artigo em questão era uma sugestão para o Concrete Mathematics, que é o meu livro de matemática predileto. Sou fã desse livro desde os 17 anos, quando o usava como referência para estudar para a Olimpíada de Matemática.

Quem já leu sabe que o livro contém mais de dez demonstrações diferentes da fórmula para a soma dos quadrados. Para ##n\ge 0##, a fórmula é:
$$\sum_{1\le x\le n} x^2=\frac{n(n+1)(2n+1)}{6}$$
As demonstrações vão desde as mais simples (usando indução finita), até as mais complexas (usando a fórmula de Euler-MacLaurin). Porém, uns anos atrás, eu bolei uma demonstração nova que é provavelmente a mais curta demonstração de todas! Ela está na caixa azul abaixo:


Testando valores pequenos, notamos que a fórmula é verdadeira para ##n=0##, ##n=1##, ##n=2##, ##n=3##. Logo, é verdadeira para qualquer ##n##.

Sim, eu sei o que você está pensando: "Ricbit, você tá doido? Só porque funciona para alguns números não quer dizer que funciona para todos os números!".

A preocupação é válida: o que mais tem na matemática são fórmulas que funcionam para números pequenos mas falham para números grandes. Um exemplo famoso é o polinômio sortudo de Euler, ##k^2-k+41##, que produz apenas números primos para ##k=1##, ##k=2##, ##k=3##, etc., mas falha lá na frente quando ##k=41##.

Porém, nesse caso, testar números pequenos é suficiente para demonstrar a fórmula! Para entender o motivo precisamos de dois fatos sobre cálculo e polinômios.


O Cálculo Discreto


Quem já estudou cálculo com certeza sabe algumas integrais de cabeça. Por exemplo, a integral do monômio:
$$\int x^n\;dx=\frac{x^{n+1}}{n+1}$$
O que você talvez não saiba é que o cálculo que a gente aprende nas faculdades de exatas é só um tipo de cálculo: o Cálculo Contínuo. Existem outros tipos, como o Cálculo Discreto, que lida com somatórias ao invés de integrais. Esse cálculo possui várias fórmulas análogas ao cálculo mais comum. Em especial, ele tem uma fórmula análoga à integral do monômio:
$$\sum x^{\underline{n}}\;\delta x=\frac{x^{\underline{n+1}}}{n+1}$$
Nesse fórmula o monômio não usa a potência simples, ele usa a potência fatorial, que é definida da seguinte maneira:
$$x^{\underline n}=x(x-1)(x-2)\dots(x-n+1)$$
É como se fosse um fatorial, mas ao invés de ir até o fim, você pega só os ##n## primeiros termos. (Para pronunciar ##x^{\underline n}##,  você fala "x elevado a n caindo").

Para converter uma potência fatorial em uma potência normal, você pode abrir a definição e multiplicar todos os termos, mas isso dá trabalho. É mais fácil ter à mão uma tabela com os números de Stirling (que vêm em dois tipos, o primeiro tipo ##{n\brack k}##, e o segundo tipo ##{n\brace k}##). Esses números são fáceis de calcular porque eles obedecem uma regra similar ao triângulo de Pascal.  Tendo a tabela, as fórmulas abaixo fazem a conversão:
$$\begin{align*}
x^{\underline{n}}&=\sum_k{n\brack k}(-1)^{n-k}x^k\\
x^{n}&=\sum_k{n\brace k}x^{\underline{k}}\end{align*}$$
Usando as fórmulas acima e alguma paciência, você consegue demonstrar a fórmula da soma dos quadrados (converta ##x^2## em potências fatoriais, use a fórmula de somatória do cálculo discreto, depois converta de volta as potências fatoriais em potências tradicionais).

Mas isso dá muito trabalho. Ao invés disso, note que as fórmulas acima tem uma consequência interessante: com elas é possível ver que a somatória de um polinômio de grau ##n## sempre vai ser um polinômio de grau ##n+1##, e em especial a somatória de ##x^2## vai ser um polinômio de grau 3, a gente só não sabe qual polinômio ainda. Mas aí temos outro truque nas mangas!

Interpolação de polinômios


Certo, eu não sei qual é o polinômio de grau 3 que resolve a somatória. Vamos então escrever esse polinômio na forma ##P(n)=an^3+bn^2+cn+d##. Apesar de não conhecermos o polinômio, é fácil descobrir os pontos por onde ele passa, basta calcular a somatória!

Para valores pequenos de ##n##, podemos tabelar ##P(0)=0##, ##P(1)=1##, ##P(2)=1+4=5##, ##P(3)=1+4+9=14##, agora podemos achar os coeficientes usando álgebra linear:
$$\left[\begin{array}{c}0\\1\\5\\14\end{array}\right]=
\left[\begin{array}{cccc}0^3&0^2&0^1&1\\1^3&1^2&1^1&1\\2^3&2^2&2^1&1\\3^3&3^2&3^1&1\end{array}\right]
\left[\begin{array}{c}a\\b\\c\\d\end{array}\right]$$
Novamente, dá muito trabalho. Ao invés disso, notamos que um polinômio de grau ##n## fica completamente determinado por ##n+1## pontos, e agora podemos finalmente entender a demonstração inicial!

Olhe novamente para o que queremos demonstrar:
$$\sum_{1\le x\le n} x^2=\frac{n(n+1)(2n+1)}{6}$$
De um lado, temos uma somatória de polinômios de grau 2, que sabemos que é um polinômio de grau 3. Do outro lado, temos um polinômio cujo grau também é 3. Para provar que esses dois polinômios são iguais, é suficiente testar os dois polinômios em quatro pontos. Escolhemos os mais fáceis que são ##n=0##, ##n=1##, ##n=2##, ##n=3##, como a fórmula funciona para esses quatro valores, então funciona para todos os valores, QED.

O cheque do Knuth


Quando eu presenteei o artigo com essa demonstração para o Knuth, em segundos após a leitura ele comentou: "Você podia ter usado -1 aqui!".

É verdade! Essa demonstração é tão curta que dá para fazer de cabeça, mas as contas para ##n=3## ficam meio chatinhas. Ao invés disso, você pode usar -1 no lugar de 3, e aí fica bem mais fácil: do lado esquerdo a soma é vazia e dá 0, do lado direito o termo ##(n+1)## zera, logo o resultado dá zero também. Você precisa expandir o domínio da fórmula, de ##n\ge 0## para ##n\ge -1##, o que significa que a fórmula provada com -1 é até melhor que a fórmula provada com 0!

Tradicionalmente, o Knuth oferece $0x1 hexadólar para quem acha um erro em um artigo dele, e $0x0.20 para sugestões. Pela simplificação que ele sugeriu, eu achei pertinente oferecer um cheque também!


Aqui o detalhe do cheque. O Knuth emite cheques pelo banco fictício de San Serriffe (eu tenho $0x5.80 lá atualmente). Os meus cheques eu resolvi emitir pelo Banco de Dados:


O Knuth adorou o cheque! Ele ficou impressionado em como eu consegui fazer tão rápido um cheque que parece mesmo um cheque, mas o segredo é simples: quem fez fez a arte foi a Ila Fox, eu só imprimi no hotel onde estava, usando papel reciclado.

Sem dúvida foi a melhor festa de aniversário que já participei! O Knuth já tinha feito uma festa dessas quando completou 64 anos, e para manter a simetria ele disse que vai fazer outra quando completar 96 anos. Eu certamente estarei lá mais uma vez :)

por Ricardo Bittencourt (noreply@blogger.com) em 19 de January de 2018 às 14:36

December 24, 2017

PythonClub

Programação funcional com Python #2 - Iteraveis e iteradores

2. Iteráveis e iteradores

O que são iteráveis? Basicamente e a grosso modo, iteráveis em python são todos os objetos que implementam o método __getitem__ ou __iter__. Beleza, vamos partir do simples.

Quase todos os tipos de dados em python são iteráveis, por exemplo: listas, strings, tuplas, dicionários, conjuntos, etc...

Vamos aos exemplos, eles são sempre mágicos:

lista = [1, 2, 3, 4, 5]

# iteração
for x in lista:
    print(x)

# 1
# 2
# 3
# 4
# 5

Era só isso? Sim, nem doeu, fala a verdade.

Em python, o comando for nos fornece um iterador implícito. O que? Não entendi.

O laço for em python itera em cada elemento da sequência. Como no exemplo, o for, ou foreach, no caso vai passando por cada elemento da sequência. Não é necessária a implementação de um index como na linguagem C, onde a iteração é explícita:

for (i = 0; i > 10; i++){
    sequencia[i];
}

2.1 __getitem__

O padrão de projeto iterator em python já vem implementado por padrão, como já foi dito antes. Basta que um objeto tenha os métodos __iter__ ou __getitem__ para que um laço possa ser utilizado.

Vamos exemplificar:

class iteravel:
    """
    Um objeto que implementa o `__getitem__` pode ser acessado por posição
    """
    def __init__(self, sequencia):
        self.seq = sequencia

    def __getitem__(self, posicao):
        """
        Por exemplo, quando tentamos acessar um elemento da sequência usando
        slice:
            >>> iteravel[2]

        O interpretador python chama o __getitem__ do objeto e nos retorna
            a posição solicitada

        um exemplo:
        IN: lista = [1, 2, 3, 4]
        IN: lista[0]
        OUT: 1
        """
        return self.seq[posicao]

Então, pode-se compreender, sendo bem rústico, que todos os objetos que implementam __getitem__ são iteráveis em python.

2.2 __iter__

Agora os objetos que implementam __iter__ tem algumas peculiaridades. Por exemplo, quando o iterável (vamos pensar no for) chamar a sequência, ela vai pedir o __iter__ que vai retornar uma instância de si mesmo para o for e ele vai chamar o __next__ até que a exceção StopIteration aconteça.

Uma classe que implementa __iter__:

class iteravel:
    def __init__(self, sequencia):
       self.data = sequencia

    def __next__(self):
        """
        Neste caso, como o método pop do objeto data é chamado
            (vamos pensar em uma lista) ele vai retornar o primeiro elemento
            (o de index 0) e vai remove-lo da lista

        E quando a sequência estiver vazia ele vai nos retornar um StopIteration
            O que vai fazer com que a iteração acabe.

        Porém, estamos iterando com pop, o que faz a nossa iteração ser a única,
            pois ela não pode ser repetida, dado que os elementos foram
            removidos da lista
        """
        if not self.sequencia:
           raise StopIteration
        return self.sequencia.pop()

    def __iter__(self):
        return self

Como é possível notar, o objeto com __iter__ não necessita de um __getitem__ e vise-versa. As diferenças partem do ponto em que um pode ser acessado por index/slice e outro não. Também um bom ponto é que nesse nosso caso, removemos os elementos da sequência, ou seja, ela se torna descartável.

Esse conceito, de ser descartado, pode parecer um pouco estranho no início, mas economiza muita memória. E, como estamos falando de programação funcional, pode-se dizer que nossa sequência se torna imutável, pois não existe uma maneira de mudar os valores contidos na lista do objeto.

Seguem dois links maravilhosos explicando sobre iteração em python:

O primeiro é a PEP sobre as estruturas dos iteráveis e o segundo um video do Guru Luciano Ramalho explicando tudo sobre iteradores.

Ah... Ia quase me esquecendo, se você não entendeu muita coisa sobre os dunders, você pode ler o Python data model. Obs: não me responsabilizo pelo programador melhor que você sairá desta página.

Embora esse tópico seja talvez o mais curto, ele vai ser de fundamental importância para o entendimento de um pouco de tudo nesse 'Curso'. É sério. Vamos entender como trabalhar com iteráveis de uma maneira bonita no próximo tópico.

por Eduardo Mendes em 24 de December de 2017 às 00:50

December 16, 2017

Filipe Saraiva

Sprint do KDE Edu 2017

Dois meses atrás participei do sprint do KDE Edu em Berlim. Essa foi a primeira vez que participei de um sprint do KDE (pois é, sou contribuidor do KDE desde 2010 e nunca tinha ido a um sprint!) e por conta disso estava bastante animado com o que iria encontrar.

KDE Edu é um guarda-chuva específico para softwares educativos do KDE. O projeto tem um monte deles, e essa é a principal suíte de softwares educativos no mundo do software livre. Apesar disso, o KDE Edu tem recebido pouca atenção no quesito organização. Um exemplo são os próprios sprints: o último ocorreu há muitos anos atrás, o website do projeto está com alguns bugs, entre outros problemas.

Portanto, esse sprint não foi apenas uma oportunidade para trabalhos de desenvolvimento (o que se espera desse tipo de encontro), mas também um bom momento para muito trabalho na parte de organização do projeto.

Nesse aspecto, discutimos sobre o rebranding de alguns dos softwares mais relacionados com trabalho universitário do que com a “educação” em si, como o Cantor ou o Labplot. Há um desejo de se criar algo como um KDE Research/Science de forma a colocar todos esses softwares e outros como o Kile e KBibTex sob um mesmo guarda-chuva. Há uma discussão sobre esse tema em andamento.

Outro tópico também discutido foi um novo site, mais direcionado a ensinar como utilizar softwares do KDE no contexto educacional do que apenas apresentar uma lista de softwares. Acredito que precisamos implementar essa ideia até para termos uma entrada própria na página de produtos do KDE.

Em seguida, os desenvolvedores do sprint concordaram com a política de multi-sistemas operacionais para o KDE Edu. Softwares do KDE podem ser compilados e distribuídos para usuários de diferentes sistemas operacionais, não apenas Linux. Durante o sprint, alguns desenvolvedores trabalharam no desenvolvimento de instaladores para Windows, Mac OS, no port de aplicações para Android, e mesmo na criação de instaladores independentes para qualquer distribuição Linux usando flatpak.

Ainda relacionado aos trabalhos organizativos, criei uma regra para enviar e-mails para a lista de e-mails do KDE Edu para cada novo Differential Revision nos softwares do projeto no Phabricator. Desculpem devs, nossas caixas de e-mail estão cheias por minha culpa. 🙂

Já nos trabalhos relacionados a desenvolvimento, foquei-me em trabalhar pesado no Cantor. Primeiro, fiz alguns trabalhos de triagem de tarefas na workboard, fechando, abrindo, e colocando mais informações em algumas delas. Em seguida, revisei alguns trabalhos feitos por Rishabh Gupta, meu estudante durante o GSoC 2017. Ele portou o backend de Lua e R para QProcess, que estarão disponíveis logo mais.

Após isso trabalhei no port do backend de Python 3 para usar a API Python/C. Esse é um trabalho em andamento e espero finalizá-lo para lançamento com a versão 18.04.

E claro, além desse monte de trabalho nos divertimos com cervejas e comidas alemãs (e alguma comida americana, chinesa, árabe, e italiana também). Algo legal foi ter completado meus 31 anos no primeiro dia do sprint, portanto obrigado KDE por ter vindo à minha festa repleta de código-fonte, boas cervejas e pratos de comida com carne de porco. 🙂

Finalizando, é sempre um prazer encontrar outros  gearheads como os amigos espanhóis Albert e Aleix, o único outro usuário Mageia que já encontrei pessoalmente em minha vida Timothée, meu aluno do GSoC Rishabh, meu camarada Sandro, e os novos amigos Sanjiban e David.

Obrigado KDE e.V por fornecer os recursos necessários para que o sprint acontecesse e valeu Endocode por sediar o evento.

por Filipe Saraiva em 16 de December de 2017 às 19:32

December 08, 2017

PythonClub

Programação funcional com Python #1 - Funções

1. Funções

Como nem tudo são flores, vamos começar do começo e entender algumas características das funções do python (o objeto função) e dar uma revisada básica em alguns conceitos de função só pra gente não se perder no básico depois. Então o primeiro tópico vai se limitar a falar da estrutura básica das funções em python, sem entrar profundamente em cada um dos tópicos. Será uma explanação de código e abrir a cabeça para novas oportunidades de código mais pythonicos e que preferencialmente gere menos efeito colateral. Mas calma, não vamos ensinar a fazer funções, você já está cheio disso.

1.1 Funções como objeto de primeira classe

Funções como objeto de primeira classe, são funções que se comportam como qualquer tipo nativo de uma determinada linguagem. Por exemplo:

# uma lista

lista = [1, 'str', [1,2], (1,2), {1,2}, {1: 'um'}]

Todos esses exemplos são tipos de objetos de primeira classe em Python, mas no caso as funções também são. Como assim? Pode-se passar funções como parâmetro de uma outra função, podemos armazenar funções em variáveis, pode-se definir funções em estruturas de dados:

# Funções como objeto de primeira classe

func = lambda x: x # a função anônima, lambda, foi armazenada em uma variável

def func_2(x):
    return x + 2

lista = [func, func_2] # a variável que armazena a função foi inserida em uma estrutura, assim como uma função gerada com def

lista_2 = [lambda x: x, lambda x: x+1] # aqui as funções foram definidas dentro de uma estrutura

Como é possível notar, em python, as funções podem ser inseridas em qualquer contexto e também geradas em tempo de execução. Com isso nós podemos, além de inserir funções em estruturas, retornar funções, passar funções como parâmetro (HOFs), definir funções dentro de funções(closures) e assim por diante. Caso você tenha aprendido a programar usando uma linguagem em que as funções não são objetos de primeira classe, não se assuste. Isso faz parte da rotina comum do python. Preferencialmente, e quase obrigatoriamente, é melhor fazer funções simples, pequenas e de pouca complexidade para que elas não sofram interferência do meio externo, gerem menos manutenção e o melhor de tudo, possam ser combinadas em outras funções. Então vamos lá!

1.2 Funções puras

Funções puras são funções que não sofrem interferência do meio externo. Vamos começar pelo exemplo ruim:

valor = 5

def mais_cinco(x):
    return x + valor

assert mais_cinco(5) == 10 # True

valor = 7

assert mais_cinco(5) == 10 # AssertionError

mais_cinco() é o exemplo claro de uma função que gera efeito colateral. Uma função pura deve funcionar como uma caixa preta, todas as vezes em que o mesmo input for dado nela, ela terá que retornar o mesmo valor. Agora vamos usar o mesmo exemplo, só alterando a linha do return:

valor = 5

def mais_cinco(x):
    return x + 5

assert mais_cinco(5) == 10 # True

valor = 7

assert mais_cinco(5) == 10 # True

Pode parecer trivial, mas muitas vezes por comodidade deixamos o meio influenciar no comportamento de uma função. Por definição o Python só faz possível, e vamos falar disso em outro tópico, a leitura de variáveis externas. Ou seja, dentro do contexto da função as variáveis externas não podem ser modificadas, mas isso não impede que o contexto externo a modifique. Se você for uma pessoa inteligente como o Jaber deve saber que nunca é uma boa ideia usar valores externos. Mas, caso seja necessário, você pode sobrescrever o valor de uma variável no contexto global usando a palavra reservada global. O que deve ficar com uma cara assim:

valor = 5

def teste():
    global valor # aqui é feita a definição
    valor = 7

print(valor) # 7

Só lembre-se de ser sempre coerente quando fizer isso, as consequências podem ser imprevisíveis. Nessa linha de funções puras e pequeninas, podemos caracterizar, embora isso não as defina, funções de ordem superior, que são funções que recebem uma função como argumento, ou as devolvem, e fazem a chamada das mesmas dentro do contexto da função que a recebeu como parâmetro. Isso resulta em uma composição de funções, o que agrega muito mais valor caso as funções não gerem efeitos colaterais.

1.3 Funções de ordem superior (HOFs)

Funções de ordem superior são funções que recebem funções como argumento(s) e/ou retornam funções como resposta. Existem muitas funções embutidas em python de ordem superior, como: map, filter, zip e praticamente todo o módulo functools import functools. Porém, nada impede de criarmos novas funções de ordem superior. Um ponto a ser lembrado é que map e filter não tem mais a devida importância em python com a entrada das comprehensions (embora eu as adore), o que nos faz escolher única e exclusivamente por gosto, apesar de comprehensions serem mais legíveis (vamos falar disso em outro contexto), existem muitos casos onde elas ainda fazem sentido. Mas sem me estender muito, vamos ao código:

func = lambda x: x+2 # uma função simples, soma mais 2 a qualquer inteiro

def func_mais_2(funcao, valor):
    """
    Executa a função passada por parâmetro e retorna esse valor somado com dois

    Ou seja, é uma composição de funções:

    Dado que func(valor) é processado por func_func:
        func_mais_2(func(valor)) == f(g(x))
    """
        return funcao(valor) + 2

Um ponto a tocar, e o que eu acho mais bonito, é que a função vai retornar diferentes respostas para o mesmo valor, variando a entrada da função. Nesse caso, dada a entrada de um inteiro ele será somado com 2 e depois com mais dois. Mas, vamos estender este exemplo:

func = lambda x: x + 2 # uma função simples, soma mais 2 a qualquer inteiro

def func_mais_2(funcao, valor):
    """
    Função que usamos antes.
    """
        return funcao(valor) + 2

assert func_mais_2(func, 2) == 6 # true

def func_quadrada(val):
    """
    Eleva o valor de entrada ao quadrado.
    """
    return val * val

assert func_mais_2(func_quadrada, 2) == 6 # true

1.3.1 Um exemplo usando funções embutidas:

Muitas das funções embutidas em python são funções de ordem superior (HOFs) como a função map, que é uma das minhas preferidas. Uma função de map recebe uma função, que recebe um único argumento e devolve para nós uma nova lista com a função aplicada a cada elemento da lista:

def func(arg):
    return arg + 2

lista = [2, 1, 0]

list(map(func, lista)) == [4, 3, 2] # true

Mas fique tranquilo, falaremos muito mais sobre isso.

1.4 __call__

Por que falar de classes? Lembre-se, Python é uma linguagem construída em classes, e todos os objetos que podem ser chamados/invocados implementam o método __call__:

Em uma função anônima:

func = lambda x: x

'__call__' in dir(func) # True

Em funções tradicionais:

def func(x):
    return x

'__call__' in dir(func) # True

Isso quer dizer que podemos gerar classes que se comportam como funções?

SIIIIIM. Chupa Haskell

Essa é uma parte interessante da estrutura de criação do Python a qual veremos mais em outro momento sobre introspecção de funções, mas vale a pena dizer que classes, funções nomeadas, funções anônimas e funções geradoras usam uma base comum para funcionarem, essa é uma das coisas mais bonitas em python e que em certo ponto fere a ortogonalidade da linguagem, pois coisas iguais tem funcionamentos diferentes, mas facilita o aprendizado da linguagem, mas não é nosso foco agora.

1.5 Funções geradoras

Embora faremos um tópico extremamente focado em funções geradoras, não custa nada dar uma palinha, não?

Funções geradoras são funções que nos retornam um iterável. Mas ele é lazy (só é computado quando invocado). Para exemplo de uso, muitos conceitos precisam ser esclarecidos antes de entendermos profundamente o que acontece com elas, mas digo logo: são funções lindas <3

Para que uma função seja geradora, em tese, só precisamos trocar o return por yield:

def gen(lista):
    for elemento in lista:
        yield elemento

gerador = gen([1, 2, 3, 4, 5])

next(gerador) # 1
next(gerador) # 2
next(gerador) # 3
next(gerador) # 4
next(gerador) # 5
next(gerador) # StopIteration

Passando bem por cima, uma função geradora nos retorna um iterável que é preguiçoso. Ou seja, ele só vai efetuar a computação quando for chamado.

1.6 Funções anônimas (lambda)

Funções anônimas, ou funções lambda, são funções que podem ser declaradas em qualquer contexto. Tá... Todo tipo de função em python pode ser declarada em tempo de execução. Porém funções anônimas podem ser atribuídas a variáveis, podem ser definidas dentro de sequências e declaradas em um argumento de função. Vamos olhar sua sintaxe:

lambda argumento: argumento

A palavra reservada lambda define a função, assim como uma def. Porém em uma def quase que instintivamente sempre quebramos linha:

def func():
  pass

Uma das diferenças triviais em python é que as funções anônimas não tem nome. Tá, isso era meio óbvio, mas vamos averiguar:

def func():
  pass

func.__name__ # func

lambda_func = lambda arg: arg

lambda_func.__name__ # '<lambda>'

O resultado '<lambda>' será o mesmo para qualquer função. Isso torna sua depuração praticamente impossível em python. Por isso os usuários de python (e nisso incluo todos os usuários, até aqueles que gostam de funcional) não encorajam o uso de funções lambda a todos os contextos da linguagem. Mas, em funções que aceitam outra funções isso é meio que uma tradição, caso a função (no caso a que executa o código a ser usado pelo lambda) não esteja definida e nem seja reaproveitada em outro contexto. Eu gosto de dizer que lambdas são muito funcionais em aplicações parciais de função. Porém, os lambdas não passam de açúcar sintático em Python, pois não há nada que uma função padrão (definida com def), não possa fazer de diferente. Até a introspecção retorna o mesmo resultado:

def func():
  pass

type(func) # function

lambda_func = lambda arg: arg

type(lambda_func) # function

Uma coisa que vale ser lembrada é que funções anônimas em python só executam uma expressão. Ou seja, não podemos usar laços de repetição (while, for), tratamento de exceções (try, except, finally). Um simples if com uso de elif também não pode ser definido. Como sintaticamente só são aceitas expressões, o único uso de um if é o ternário:

valor_1 if condicao else valor_2

O que dentro de um lambda teria essa aparência:

func = lambda argumento: argumento + 2 if argumento > 0 else argumento - 2

Funções lambda também podem ter múltiplos argumentos, embora seu processamento só possa ocorrer em uma expressão:

func = lambda arg_1, arg_2, arg_3: True if sum([arg_1, arg_2, arg_3]) > 7 else min([arg_1, arg_2, arg_3])

Embora essa seja uma explanação inicial sobre as funções anônimas, grande parte dos tópicos fazem uso delas e vamos poder explorar melhor sua infinitude.

Mas por hoje é só e no tópico seguinte vamos discutir, mesmo que superficialmente, iteradores e iteráveis e suas relações com a programação funcional.

por Eduardo Mendes em 08 de December de 2017 às 15:30

November 28, 2017

Bruno Cezar Rocha

py2rs - from Python to Rust - Reference Guide

py2rs

From Python into Rust

let x = Rust::from("Python");

A quick reference guide for the Pythonista in process of becoming a Rustacean.

NOTE The original repository for this article is on https://github.com/rochacbruno/py2rs <-- READ THERE FOR UPDATED VERSION

Monty Python - Season 3 - Episode 49

The sketch

Mrs. Jalin: George.
Mr. Jalin: Yes, Gladys.
Mrs. Jalin: There's a man at the door with a moustache.
Mr. Jalin: Tell him I've already got one. (Mrs. Jalin hits him hard with a newspaper) 
          All right, all right. What's he want then?
Mrs. Jalin: He says do we want a documentary on crustaceans.
Mr. Jalin: Crustaceans!
Mrs. Jalin: Yes.
Mr. Jalin: What's he mean, crustaceans?
Mrs. Jalin: CRUSTACEANS!! GASTROPODS! LAMELLIBRANCHS! CEPHALOPODS!
...
Ok...watch it later... let's learn some Rust now...

TOC

Getting Started with Rust

Assuming you already know what is Rust and already decided to start learning it. Here are some steps for you to follow:

  1. Take a tour of Rust Syntax and Coding Style
    https://learnxinyminutes.com/docs/rust/
  2. Watch some screencasts to get basics of Ownership &Borrowing concept
    http://intorust.com/
  3. Follow this set of runnable examples to understand how everything fit together
    https://rustbyexample.com
  4. Now it is time to read your first book, you can pick:

  5. Read some real examples

  6. Patterns and Good Practices

Exercices

Time to put your new knowledge in action solving some exercices.

1) Exercism.io
Register a new account on exercism.io (using github auth)
Install exercism command line client on your computer
Solve some exercices: http://www.exercism.io/languages/rust/about

2) Rust Playground
Run Live Rust Code in the browser with https://play.rust-lang.org/

Getting updated

Now I assume you are addicted to Rust and you want to be updated about averything around it, here are some good links to follow.

1) This Week in Rust Newsletter
https://this-week-in-rust.org/
https://twitter.com/thisweekinrust 2) Reddit
http://reddit.com/r/rust (serious sub-reddit)
http://reddit.com/r/rustjerk (almost memes only)
3) Official Twitter
https://twitter.com/rustlang

Interact with other Rustaceans

Don't be afraid, the Rustaceans are a very receptive species and are cozy with the Pythonistas.

Community links: https://www.rust-lang.org/en-US/community.html

Local

Additional learning resources

Facts

More facts? send a question here or send a Pull Request adding an interest fact to this list.

ferris

Glossary of terms

Term Definition
crate A rust distributable package
ferris The unofficial Crab Mascot
Rustacean The Rust programmer or evangelist or enthusiastic
nightly The unstable toolchain of the Rust compiler
impl Implementation

py2rs

From Python into Rust

let x = Rust::from("Python");

A quick reference guide for the Pythonista in process of becoming a Rustacean.

General

Python Definition Rust
PEP8 Guidelines and conventions RustAPI Guidelines
PEPS Enhancement Proposals / RFC Rust RFCs
PSF Organization / Foundation Mozilla Research
PyCon Main Conference RustConf
Guido Van Rossum Creator Graydon Hoare
1989 First appeared 2010
1991 First Release 2012
PSF License Apache 2.0 and MIT
C Implemented in Rust
.py, .pyw, .pyc File Extensions .rs, .rlib
http://github.com/python/cpython Repository https://github.com/rust-lang/rust
Pyladies, AfroPython Diversity and Inclusion initiative RustBridge
comp.lang.Python Official Users Forum users.rust-lang.org

Environment Tools

Python Definition Rust
requirements.txt Official dependency tracker file Cargo.toml
setup.py Official installator / distributor file Cargo.toml
PyPI Library Repositoty Crates.io
pip Library installation Cargo
setuptools Library distribution Cargo
pbr Library distribution Cargo
pipenv Dependency manager Cargo
twine Package uploader Cargo and Semantic
venv * Isolated environments Cargo
pyinstaller Generate Standalone Executables Cargo
pyenv Install and manage versions of language rustup
sphinx Generate documentation from code rustdoc and Cargo
python Interpreter / Compiler rustc and Cargo
ipython REPL rusti
ipdb Debugger rust-gdb

Libraries and Frameworks

Python Definition Rust
urllib * HTTP calls hyper
requests simplified HTTp calls reqwest
json JSON Parsing loading and dumping serde
pyYAML YAML Parsing loading and dumping serde
lxml XML Parsing loading and dumping RustyXML
csv * CSV parsing rust-csv
datetime * & Dateutils Date & time Chrono
click and argparse CLI Framework clap
docopt CLi Framework docopt
re * Regular Expressions regex
subprocess * Run external commands crossbeam and Rayon
logging * Logging log
Pathlib * Path manipulation fs and fs_extra
cryptography crytography crypto
pickle * Object Serialization RON
heapq * Heap Queue BinaryHeap *
bottle Minimal Web Framework Iron
flask Web Framework Rocket
django Full Stack Web Framrwork Gotham
SQL Alchemy Relational Database ORM Diesel
Pymongo Mongo DB driver mongodb
Jinja 2 Template Engine Tera
pygtk GTk desktop development gtk
pyside QT desktop development rust-qt
pygame 2D UI library / gaming Conrod & Piston
unitest2 Test framework Builtin
nose Test Runner Cargo
pytest Testing Framework and Runner Polish
Flake8 Linter Clippy
autopep8 Auto formatter rustfmt
twisted Network application framework libpnet
AsyncIO * Async application framework Tokio and futures
Pillow Image Manipulation Image
Beautiful Soup HTML Parser html5ever
Hypothesis Data Driven test framework proptest
mock Test Mocking Mockito
bioPython Bioinformathics libraries Rust Bio
Dynaconf Config management Config
itertools * Data Structure Iteration Rust Itertools
Geopython geo Spatial Data Geo Rust
ScikitLearn Machine Learning rusty-machine
mistune Markdown / Common Mark Parser cmark
celery Distributed Computation Antimony
boto AWS clients rusoto
AstroPy Astronomy atro-rust
Numpy Numeric Numeric

Applications

Python Definition Rust
Pelican Static Site generator Cobalt
ansible Infra Orchestration realize
mkdocs Generate documentation and e-books from Markdown mdBook
locust HTTP load test drill
Nameko Microservices Framework fractalide
Quokka CMS CMS NIckel CMS

Useful crates

Add Pythonic features to Rust

Python Definition Rust
{'foo': "bar"} Syntax to create a dict / hashmap maplit
__init__(self, value='default') Initializing instances with default values derive_new
itertools *stdlib Extra iterators methods itertools

Show me The code

From Python to Rust by examples

You can copy-paste and run the Rust examples in https://play.rust-lang.org/ and Python in https://repl.it/languages/python3

Creating a new project

Create a new project with baseic files, entry points, module initializer, dependency and installation artifacts.

Python

$ mkdir {pyproject,pyproject/src}
$ touch {pyproject/src/{__init__.py,__main__.py,program.py},pyproject/{requirements.txt,setup.py}} 
$ echo "-e ." >> pyproject/requirements.txt
$ echo "from setuptools import setup" >> pyproject/setup.py
$ echo "setup(author=..., name=...)" >> pyproject/setup.py

Rust

$ cargo new my-rust-program

Installing new libraries/crates

Python

$ pip install foo

Rust

$ cargo install foo

Running / Compiling

Python

$ python my_python_program.py

Rust

$ cargo run

Hello World

Python

if __name__ == "__main__":
    print("Hello, World")

Rust

fn main() {
  println!("Hello, World");
}

Types and Declarations

Create new objects, values on basic primitive types and also data structures.

Python

age = 80
name = 'daffy'
weight = 62.3
loons = ['bugs', 'daffy', 'taz']
ages = {  # Ages for 2017
    'daffy': 80,
    'bugs': 79,
    'taz': 63,
}

Rust

use std::collections::HashMap;

fn main() {
    let age = 80;
    let name = "daffy";
    let weight = 62.3;
    let mut loons = vec!["bugs", "daffy", "taz"];

    let mut ages = HashMap::new();  // Ages for 2017
    ages.insert("daffy", 80);
    ages.insert("bugs", 79);
    ages.insert("taz", 63);
}

Define a function

Defining a function that takes 2 integer arguments and returns its sum.

Python

def add(a, b):
    "Adds a to b"""
    return a + b

Rust

// Adds a to b
fn add(a: i32, b: i32) -> i32 {
  a + b
}

List/Slice

Creating a list, adding new elements, gettings its length, slicing by index, itarating using for loop and iterating with enumerator.

Python

names = ['bugs', 'taz', 'tweety']
print(names[0])  # bugs
names.append('elmer')
print(len(names))  # 4
print(names[2:])  # ['tweety', 'elmer']

for name in names:
    print(name)

for i, name in enumerate(names):
    print('{} at {}'.format(name, i))

Rust

fn main() {
    let mut names = vec!["bugs", "taz", "tweety"];
    println!("{}", names[0]);  // bugs
    names.push("elmer");
    println!("{}", names.len());  // 4
    println!("{:?}", &names[2..]);  // ["tweety", "elmer"]

    for name in &names {
        println!("{}", name);
    }

    for (i, name) in names.iter().enumerate() {
        println!("{} at {}", i, name);
    }
}

Dict/Map

Create new dictionaries (hash maps), adding new keys and values, changing values, getting by key, checking if a key is containing, etc.

Python


# Creating a new dict and populating it
ages = {}
ages['daffy'] = 80
ages['bugs'] = 79
ages['taz'] = 63

# or doing the same using a for loop
ages = {}
for name, age in [("daffy", 80), ("bugs", 79), ("taz", 63)]:
    ages[name] = age

# or initializing from a list
ages = dict([("daffy", 80), ("bugs", 79), ("taz", 63)])

# or passing key values on creation
ages = {  # Ages for 2017
    'daffy': 80,
    'bugs': 79,
    'taz': 63,
}

ages['elmer'] = 80
print(ages['bugs'])  # 79
print('bugs' in ages)  # True

del ages['taz']

for name in ages:  # Keys
    print(name)

for name, age in ages.items():  # Keys & values
    print('{} is {} years old'.format(name, age))

Rust

use std::iter::FromIterator;
use std::collections::HashMap;

fn main() {

    // Creating a new HashMap and populating it
    let mut ages = HashMap::new();  // Ages for 2017
    ages.insert("daffy", 80);
    ages.insert("bugs", 79);
    ages.insert("taz", 63);

    // or doing the same using a loop
    let mut ages = HashMap::new();
    for &(name, age) in [("daffy", 80), ("bugs", 79), ("taz", 63)].iter() {
        // For non-Copy data, remove & and use iter().clone()
        ages.insert(name, age);
    }

    // or initializing from Array
    let mut ages: HashMap<&str, i32> =  // Ages for 2017
        [("daffy", 80), 
         ("bugs", 79), 
         ("taz", 63)]
        .iter().cloned().collect();

    // or initializing from Vec (Iterator)
    let mut ages: HashMap<&str, i32> =  // Ages for 2017
        HashMap::from_iter(
            vec![
               ("daffy", 80),
               ("bugs", 79),
               ("taz", 63)
            ]
        );

    ages.insert("elmer", 80);
    println!("{}", ages["bugs"]);  // 79
    println!("{}", ages.contains_key("bugs")); // true
    ages.remove("taz");


    for name in ages.keys() {  // Keys
      println!("{}", name);
    }

    for (name, age) in &ages {  // Keys & values
      println!("{} is {} years old", name, age);
    }

}

Pythonic alternative to dict/map in Rust

You can use the maplit crate to load hashmap! macro to have an efficient sugared (a.k.a Pythonic) syntax!

# Cargo.toml
[dependencies]
maplit = "*"

then

#[macro_use] extern crate maplit;

let map = hashmap!{
    "daffy" => 80,
    "bugs" => 79,
    "taz" => 63,
};

set / HashSet

Create a set (a hash of unique keys), add new keys and compute intersection, difference and union

Python


# creating and populating
colors = set()
colors.add("red")
colors.add("green")
colors.add("blue")
colors.add("blue")

# using literal syntax
colors = {'red', 'green', 'blue', 'blue'}

# from an iterator
colors = set(['red', 'green', 'blue', 'blue'])


# deduplication
print(colors)  # {"blue", "green", "red"}

# operations
colors = {'red', 'green', 'blue', 'blue'}
flag_colors = {"red", "black"}

# difference
colors.difference(flag_colors)  # {'blue', 'green'}

# symmetric difference
colors.symmetric_difference(flag_colors)  # {'black', 'blue', 'green'}

# intersection
colors.intersection(flag_colors)  # {'red'}

# unioin
colors.intersection(flag_colors)  # {'black', 'blue', 'green', 'red'}

Rust

use std::collections::HashSet;
use std::iter::FromIterator;

fn main() {

    // creating and populating - type inference
    let mut colors = HashSet::new();
    colors.insert("red");
    colors.insert("green");
    colors.insert("blue");
    colors.insert("blue");

    // from an iterator - explicit type
    let mut colors: HashSet<&str> = HashSet::from_iter(vec!["red", "green", "blue", "blue"]);

    // deduplication
    println!("{:?}", colors); // {"blue", "green", "red"}

    // Operations
    let mut colors: HashSet<&str> = HashSet::from_iter(vec!["red", "green", "blue", "blue"]);
    let mut flag_colors: HashSet<&str> = HashSet::from_iter(vec!["red", "black"]);

    // difference
    colors.difference(&flag_colors); // ["green", "blue"]

    // symmetric difference
    colors.symmetric_difference(&flag_colors); // ["blue", "green", "black"]

    // intersection
    colors.intersection(&flag_colors); // ["red"]

    // union
    colors.union(&flag_colors); // ["red", "blue", "green", "black"]
}

or syntax sugared using maplit crate

#[macro_use] extern crate maplit;

let colors = hashset!{"red", "green", "blue", "blue"};

while and for loops

Looping until a condition is met or over an iterable object.

Python

# While loop

counter = 0
while counter < 10:
    print(counter)
    counter += 1

# infinite while loop
while True:
    print("loop Forever!")

# infinite ehile loop with break
counter = 0
while True:
    print(counter)
    counter += 1
    if counter >= 10:
        break


# while loop with continue
counter = 0
while True:
    counter += 1
    if counter == 5:
        continue
    print(counter)
    if counter >= 10:
        break

# For loop over a list
for color in ["red", "green", "blue"]:
    print(color)

# Enumerating indexes
for  i, color in enumerate(["red", "green", "blue"]):
    print(f"{color} at index {i}")

# For in a range
for number in range(0, 100):
    print(number)  # from 0 to 99

Rust

fn main() {

    // While loop
    let mut counter = 0;
    while counter < 10 {
        println!("{}", counter);
        counter += 1;
    }

    // infinite while loop
    loop {
        println!("Loop forever!");
    }

    // infinite while loop with break
    let mut counter = 0;
    loop {
        println!("{}", counter);
        counter += 1;
        if counter >= 10 { break; }
    }

    // infinite while loop with continue
    let mut counter = 0;
    loop {
        counter += 1;
        if counter == 5 { continue; }
        println!("{}", counter);
        if counter >= 10 { break; }
    }

    // for loop over a list
    for color in ["red", "green", "blue"].iter() {
        println!("{}", color);
    }

    // Enumerating indexes
    for (i, color) in ["red", "green", "blue"].iter().enumerate() {
        println!("{} at index {}", color, i);
    }

    // for in a range
    for number in 0..100 {
        println!("{}", number);  // from 0 to 99
    }
}

Loop Labels

Rust has a looping feature which is not present on Python: Loop labels

'outer: for x in 0..10 {
    'inner: for y in 0..10 {
        if x % 2 == 0 { continue 'outer; } // continues the loop over x
        if y % 2 == 0 { continue 'inner; } // continues the loop over y
        println!("x: {}, y: {}", x, y);
    }
}

Files

Read a text file and iterate its lines printing the content, properly close the file at the end.

Python

from pathlib import Path

with open(Path("/tmp/song.txt")) as fp:
    #  Iterate over lines
    for line in fp:
        print(line.strip())

Rust

use std::io::{BufReader, BufRead};
use std::fs::File;
use std::path::Path;


fn main () {
    let fp = File::open(Path::new("/tmp/song.txt")).unwrap();
    let file = BufReader::new(&fp);
    for line in file.lines() {
        //  Iterate over lines
        println!("{}", line.unwrap());
    }
}

Exceptions/Return Error

Expecting for exceptions and identifying errors.

Python

def div(a, b):
    if b == 0:
        raise ValueError("b can't be 0")
    return a / b

# ...

try:
    div(1, 0)
except ValueError:
    print('OK')

Rust

```

---

### Concurrency

**Python**

```python
thr = Thread(target=add, args=(1, 2), daemon=True)
thr.start()

Rust

```

---

### Communicating between threads

Managing data context between threads.

**Python**

```python
from queue import Queue
queue = Queue()
# ...
# Send message from a thread
queue.put(353)


# ...
# Get message to a thread
val = queue.get()

Rust

```

---

### Sorting

Sorting lists, reversing and using a key.

**Python**

```python
names = ['taz', 'bugs', 'daffy']

# Lexicographical order
names.sort()

# Reversed lexicographical order
names.sort(reverse=True)

# Sort by length
names.sort(key=len)

Rust

```

---

### Web app with Flask / Rocket

**Python**

```python
from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
    return 'Hello Python'


if __name__ == '__main__':
    app.run(port=8080)

Rust

#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;

#[get("/")]
fn index() -> &'static str {
    "Hello Rust"
}

fn main() {
    rocket::ignite().mount("/", routes![index]).launch();
}

MISSING SOME IMPLEMENTATIONS AND EXAMPLES PLEASE CONTRIBUTE on https://github.com/rochacbruno/py2rs

HTTP Request with error handling

Python

import json
from urlib2 import urlopen

url = 'https://httpbin.org/ip'
try:
    fp = urlopen(url)
except HTTPError as err:
    msg = 'error: cannot get {!r} - {}'.format(url, err)
    raise SystemExit(msg)

try:
    reply = json.load(fp)
except ValueError as err:
    msg = 'error: cannot decode reply - {}'.format(err)
    raise SystemExit(msg)

print(reply['origin'])

Rust

```

---

### Encode and Decode JSON

**Python**

```python
data = '''{
    "name": "bugs",
    "age": 76
}'''
obj = json.loads(data)

json.dump(obj, stdout)

Rust

```


---


### Print Object for Debug/Log 

**Python**

```python
daffy = Actor(
    name='Daffy',
    age=80,
)
print('{!r}'.format(daffy))

Rust

```

---

### Object Orientation

**Python**

```python
class Cat:
    def __init__(self, name):
        self.name = name

    def greet(self, other):
        print("Meow {}, I'm {}".format(other, self.name))

# ...

grumy = Cat('Grumpy')
grumy.greet('Grafield')

Rust

rust


Credits

NOTE The original repository for this article is on https://github.com/rochacbruno/py2rs CONTRIBUTE

Created by Bruno Rocha @rochacbruno inspired by https://www.353.solutions/py2go/index.html

With contributions by:

  • Send a PR and include your name and links

por Bruno Rocha em 28 de November de 2017 às 21:32

Lauro Moura

[C] Varargs e passando eles adiante

Saindo do mundo C# e voltando para o “metal” C, uma coisa que pode parecer magia para quem nunca usou são as funções variádicas – funções com um número variável de argumentos. Quem nunca se perguntou como uma função como printf é implementada? Em Python (:heart:) nós temos os velhos conhecidos *args e **kwargs, que permitem coletar argumentos extras em uma tupla (posicionais) e um dicionários (argumentos nomeados), respectivamente. Mas em C, o processo é um pouco mais complicado, envolvendo um tipo (va_list) e três macros básicas (va_start, va_arg e va_end), todos disponíveis no header <stdarg.h>.

Exemplo básico:

Abaixo segue um pequeno exemplo de uma função – sum_all – que recebe um inteiro com um contador e uma lista variável de argumentos – que esperamos serem ints –  e retorna a soma desses argumentos variáveis.

#include <stdarg.h>
#include <stdio.h>

int sum_all(int count, ...) {
    va_list args;
    va_start(args, count);
    int acc = 0;

    for (int i=0; i < count; i++) {
        int value = va_arg(args, int);
        acc += value;
    }

    va_end(args);
    return acc;
}

int main() {
    printf("%d\n", sum_all(3, 1, 5, 1000));
}

Uma função variádica é declarada usando “” no final da lista de argumentos – a qual deve conter obrigatoriamente pelo menos um argumento com nome. Dentro do corpo da função por sua vez, você declara uma variável do tipo va_list e passa essa variável para a macro va_start junto com o nome da última variável antes da lista anônima. Esse segundo argumento é importante para o código saber de onde ele deve começar a buscar os argumentos.

Uma vez que temos a va_list inicializada, podemos então passar a pegar os argumentos propriamente ditos. Para isso utilizamos a macro va_arg, que recebe como argumento a va_list inicializada e o tipo esperado do argumento. Essa segunda informação é extremamente importante para o compilador saber qual o tamanho do argumento que ele deve buscar na lista. Ao final, va_end é chamada para limpar a va_list utilizada.

CUIDADO!

Duas observações: O compilador não faz *nenhuma* checagem de tipo entre o tipo passado numa chamada da função e o tipo que va_arg tenta extrair. Nada impede por exemplo de você chamar va_arg(args, double) dentro do loop acima e receber lixo. E diferente de Python onde você recebe uma tratável exceção, em C isso pode significar o programa simplesmente dar um belo segfault na cara do usuário.

A outra observação é quanto ao problema de se descobrir quando chegamos ao final da lista de argumentos. va_arg não dá informação nenhuma a respeito, já que ele apenas extrai da pilha – onde os argumentos são guardados – um valor do tamalho do tipo fornecido. Os dois modelos mais usados para resolver isso são usar um contador/string de formato e usar sentinelas. No primeiro caso, que é como o printf e nossa sum_all acima fazem, à medida que você vai parseando a string/incrementando o contador você sabe quando deve parar ou não. Já com o uso de sentinelas, ao parsear o argumento você determina valores (ex: NULL) que ao serem lidos indicarão que chegamos ao fim da lista.

Passando adiante

Agora digamos que você tenha uma bela função variádica mas você quer “decorar” ela com outra função sua – para fins de debug/log, por exemplo. Como podemos passar esses argumentos anônimos adiante? Passar a va_list diretamente não passaria todos os argumentos diretamente tal como seria uma chamada func(*args) em Python. Na verdade, passar a va_list é equivalente a chamar func(args) – a função recebe um único argumento com a va_list.

E é aproveitando essa última informação que uma boa prática com varargs é fornecer uma variante da função alvo, só que recebendo uma va_list. Por exemplo, temos o par printf e vprintf. A primeira é variádica e na prática é implementada em termos da segunda, que recebe a va_list. Traduzindo para nosso exemplo acima:

int vsum_all(int count, va_list args) {
    int acc = 0;
    for (int i=0; i < count; i++) {
        int value = va_arg(args, int);
        acc += value;
    }
    return acc;
}

int sum_all(int count, ...) {
    va_list args;
    va_start(args, count);

    int acc = vsum_all(count, args);

    va_end(args);
    return acc;
}

E caso você necessite utilizar uma va_list com os argumentos antes de passar eles adiantes, você pode utilizar a va_copy(mylist) para incializar uma nova va_list com uma cópia da va_list original.

o/

por lauro em 28 de November de 2017 às 02:53

November 21, 2017

Lauro Moura

C#, COM, OLE e threads

Seguindo as aventuras no mundo dos bindings C# para a EFL, um problema que enfrentei semana passada durante o port dos bindings para o Windows envolvia a famigerada API Win32. Tudo estava correndo bem até tentar mostrar algo na tela, quando os módulos de UI simplesmente se recusavam a carregar, ao contrário de quando tentávamos rodar apenas o código nativo, sem o binding. O culpado: OleInitialize.

A EFL no Windows utiliza por baixo dos panos a Win32, API tradicional para apps nativas do sistema operacional. Entre os componentes utilizados, está o velho OLE (Object Linking and Embedding), responsável por compartilhar itens entre aplicações, como por exemplo os serviços de drag and drop e clipboard, desde o começo dos anos 90. Ao longo do tempo, o OLE acabou gerando o COM (Component Object Model), que serve comunicação interprocessos para outras tecnologias do Windows – numa analogia meio “grosseira”, seria uma espécie de “DBus do Windows”, porém pelo menos 10 anos mais velho.

Para lidar com threading, o COM agrupa os objetos em apartments, que podem ser single thread – todas as chamadas a um objeto saem da mesma thread – e multi thread – cada objeto cuida de sua própria sincronização e chamadas podem vir de múltiplas threads. Entre outras coisas, o Oleinitialize cuida de inicializar o COM caso este já não esteja inicializado, e obrigatoriamente no modo single-thread. Caso a função retorne RPC_E_CHANGED_MODE, significa que o COM já foi inicializado anteriormente em modo multi-thread. E era exatamente isso que estava acontecendo.

Investigando mais um pouco, o principal suspeito era o próprio runtime do .NET. Verificando o número de threads com System.Diagnostics.Process.GetCurrentProcess().Threads.Count, um simples “hello world” indicava 6 threads em execução. E ao inspecionar o código do Mono, realmente o COM era inicializado em modo multithread. E a resposta é dada na documentação do atributo STAThreadAttribute (livre tradução):

A partir da versão 2.0 do framework .NET, o modelo de threading padrão da interoperabilidade COM depende da linguagem em que se está desenvolvendo a aplicação:

  • C++/C#: Multi-thread
  • Visual Basic: Single thread.

Prevendo esse tipo de problema, .NET oferece para C# o já mencionado atributo STAThreadAttribute, que obrigatoriamente deve decorar o Main() da aplicação para indicar que a interoperabilidade COM deve excepcionalmente ser inicializada como single thread. Para aplicações C++, a flag /CLRTHREADATTRIBUTE:STA deve ser fornecida para o linker.

o/

por lauro em 21 de November de 2017 às 19:11

November 20, 2017

PythonClub

Programação funcional com Python #0 - Saindo da zona de conforto

0. Saindo da zona de conforto

Sinta-se um vencedor, se você chegou até aqui, isso significa que quer aprender mais sobre o mundo da programação.

Aprender novos paradígmas podem te trazer muitas coisas positivas, assim como aprender linguagens diferentes, pois paradígmas e linguagens transpõem maneiras, estruturas e métodos de implementação completamente diferentes. Com isso você pode ter mais ferramentas para usar no dia a dia. Você pode aumentar sua capacidade de expressar ideias de diferentes maneiras. Eu penso que o maior limitador de um programador é a linguagem de programação em que ele tem domínio. Quando você aprende linguagens imperativas, como C, Python, Java e etc..., você se vê limitado ao escopo de criar e manipular variáveis. Não que isso seja uma coisa ruim, porém existem outras maneiras de resolver problemas e quando você tem conhecimento disso consegue avaliar melhor quando implementar cada tipo de coisa.

Você pode me dizer que aprender diferentes tipos de estruturas e maneiras de computar é uma coisa negativa pois tudo é variável nesse contexto. Mas eu penso exatamente o contrário, quanto mais você aprender da sua língua nativa, no caso estamos falando em português, maior o campo de domínio que você tem sobre como se comunicar e expressar ideias. Assim como aprender outras línguas te darão mais fundamentos para expressar ideias em outros idiomas, que não são melhores que os seu, mas diferentes e compõem diferentes estruturas, e isso pode ser libertador. Não quero me prolongar nesse assunto, mas dizer que isso pode acrescentar muito na suas habilidades cognitivas, até mesmo para usar ferramentas que você já usa no seu dia a dia.

Vamos começar fazendo uma tentativa de entender os paradígmas de programação, sem muito falatório e complicações. Um exemplo muito legal é do David Mertz em "Functional Programming in Python":

  • Usa-se programação funcional quando se programa em Lisp, Haskell, Scala, Erlang, F# etc..

  • Do mesmo modo que se usa programação imperativa quando se programada C/C++, Pascal, Java, Python etc...

  • Também quando se programa Prolog estamos programando usando o paradígma lógico.

Apesar de não ser uma definição muito elegante, talvez seja a melhor a ser dada em muitas ocasiões. Vamos tentar ser um pouco mais objetivos em relação ao estilo de computação, embora essa discussão não tenha fim:

  • O foco de usar programação imperativa está no ato de mudar variáveis. A computação se dá pela modificação dos estados das variáveis iniciais. Sendo assim, vamos pensar que tudo é definido no início e vai se modificando até que o resultado esperado seja obtido.

  • Na programação funcional, se tem a noção de que o estado deve ser substituído, no caso da avaliação, para criação de um novo 'objeto' que no caso são funções.

0.1 Mas de onde vem a programação funcional?

O florescer da programação funcional nasce no Lisp (acrônomo para List Processing) para tentar resolver alguns problemas de inteligência artificial que eram provenientes da linguística, que tinha foco em processamento de linguagem natural que por sua vez eram focados em processamento de listas em geral. Isso justifica uma grande parte do conteúdo que vamos ver aqui e seus tipos de dados variam somente entre listas e átomos. E assim foi mantido o foco de processamento de listas em todas as linguagens funcionais e suas funções e abstrações para resolver problemas relativos a listas e estruturas iteráveis. Uma curiosidade é que para quem não sabe porque em lisp existem tantos parênteses é que ele é baseado em s-expression, uma coisa que temos um "equivalente" evoluído em python, que parte dos teoremas de gramáticas livres de contexto:

(+ 4 5)

Sim, isso é uma soma em lisp. Diferente das linguagens imperativas como costumamos ver:

4 + 5

Uma assertiva pode ser feita dessa maneira:

  • Funcional (ou declarativa)
(= 4 (+ 2 2))
  • Imperativa
(2 + 2) == 4

Chega de enrolação e vamos correr com essa introdução, não viemos aqui para aprender Lisp ou C. Mas acho que parte desse contexto pode nos ajudar e muito quando formos nos aprofundar em alguns tópicos. Pretendo sempre que iniciar uma nova ferramenta da programação funcional ao menos explicar em que contexto ela foi desenvolvida e para resolver cada tipo de problema.

0.2 Técnicas usadas por linguagens funcionais

Vamos tentar mapear o que as linguagens funcionais fazem de diferente das linguagens imperativas, mas não vamos nos aprofundar nesse tópicos agora, pois são coisas às vezes complexas sem o entendimento prévio de outros contextos, mas vamos tentar só explanar pra que você se sinta empolgado por estar aqui:

  • Funções como objetos de primeira classe:

    • São funções que podem estar em qualquer lugar (em estruturas, declaradas em tempo de execução).
  • Funções de ordem superior:

    • São funções que podem receber funções como argumentos e retornar funções.
  • Funções puras:

    • São funções que não sofrem interferências de meios externos (variáveis de fora). Evita efeitos colaterais.
  • Recursão, como oposição aos loops:

    • Frequentemente a recursão na matemática é uma coisa mais intuitiva e é só chamar tudo outra vez, no lugar de ficar voltando ao ponto inicial da iteração.
  • Foco em processamento de iteráveis:

    • Como dito anteriormente, pensar em como as sequências podem nos ajudar a resolver problemas.
  • O que deve ser computado, não como computar:

    • Não ser tão expressivo e aceitar que as intruções não tem necessidade de estar explicitas todas as vezes, isso ajuda em legibilidade.
  • Lazy evaluation:

    • Criar sequências infinitas sem estourar nossa memória.

0.3 Python é uma linguagem funcional?

Não. Mas é uma linguagem que implementa muitos paradígmas e porque não usar todos de uma vez?

O objetivo desse 'conjunto de vídeos' é escrever código que gere menos efeito colateral e código com menos estados. Só que isso tudo feito na medida do possível, pois Python não é uma linguagem funcional. Porém, podemos contar o máximo possível com as features presentes do paradígma em python.

Exemplos de funcional (básicos) em python:

# Gerar uma lista da string # Imperativo
string = 'Python'
lista = [] # estado inicial

for l in string:
    lista.append(l) # cada iteração gera um novo estado

print(lista) # ['P', 'y', 't', 'h', 'o', 'n']
# Gerar uma lista da string # Funcional
string = lambda x: x

lista = list(map(str, string('Python'))) # atribuição a um novo objeto

print(lista) # ['P', 'y', 't', 'h', 'o', 'n']

Como você pode ver, depois de uma explanação básica das técnicas, a segunda implementação não sofre interferência do meio externo (Funções puras), evita loops e sua saída sem o construtor de list é lazy. Mas não se assuste, vamos abordar tudo isso com calma.

0.4 A quem esse 'curso' é destinado?

Primeiramente gostaria de dizer que roubei essa ideia dos infinitos livros da O’Reilly, que sempre exibem esse tópico. Mas vamos ao assunto. Este curso é para você que sabe o básico de Python, e quando digo básico quero dizer que consegue fazer qualquer coisa com um pouco de pesquisa na internet. O básico de programação se reduz a isso. Vamos falar sobre coisas simples e coisas mais complexas, mas pretendo manter o bom senso para que todos possam absorver o máximo de conteúdo possível.

Então, caso você venha do Python (OO ou procedural) você vai encontrar aqui uma introdução a programação funcional descontraída e sem uma tonelada de material difícil de entender. Caso você venha de linguagens funcionais como Haskell e Lisp, você pode se sentir um pouco estranho com tantas declarações, mas aprenderá a se expressar em Python. Caso você venha de linguagens funcionais modernas como Clojure e Scala, as coisas são bem parecidas por aqui.

Então tente tirar o máximo de proveito. Vamos nos divertir.

0.5 Apresentando o Jaber

Jaber é nosso aluno de mentira, mas vamos pensar que ele é um aluno que senta na primeira fileira e pergunta de tudo, sempre que acha necessário. Roubei essa ideia do livro de expressões regulares do Aurélio. Ele tem um personagem, Piazinho, e acho que toda interação com ele é sempre graciosa e tira dúvidas quando tudo parece impossível.

0.6 Sobre as referências

Não gosto muito de citar referências pois procurei não copiar texto dos livros, mas muita coisa contida neles serve de base para o entendimento de certos tópicos. Outro motivo é o nível de complexidade dos exemplos ou explicações que tentei reduzir ao máximo enquanto escrevia esses roteiros. Para um exemplo, você pode olhar o livro do Steven Lott, cheio de fórmulas e abstrações matemáticas que em certo ponto acabam comprometendo o entendimento de quem não tem uma sólida base em computação teórica ou matemática.

Como um todo, as referências serviram como guia, foi o que lí quando dúvidas para explicações surgiram. Não tiro nenhum crédito delas e as exponho para que todos saibam que existem muitos livros bons e que boa parte do que é passado aqui, foi aprendido neles.

0.7 Mais sobre o histórico das linguagens funcionais

Se você pretende realmente se aprofundar no assunto enquanto acompanha esse curso, fazer uma imersão ou coisa parecida. Tudo começa com o cálculo lambda mentalizado pelo incrível Alonzo Church. Caso você não o conheça, ele foi um matemático fantástico e teve uma carreira acadêmica brilhante. Foi o orientador de pessoas incríveis como Alan Turing, Raymond Smullyan etc...

Outro grande homem e que vale a pena mencionar e ser buscado é o Haskell Curry, um lógico que trouxe excelentes contribuições para o que chamamos hoje de programação funcional.

A primeira linguagem funcional 'oficial' (não gosto muito de dizer isso) é o Lisp (List Processing) criada pelo fenomenal John McCarthy que também vale a pena ser pesquisado e estudado.

Veremos o básico sobre os tipos de função no próximo tópico.

OBS: Referências usadas durante todos os tópicos.

por Eduardo Mendes em 20 de November de 2017 às 21:43

October 31, 2017

Thiago Avelino

Fico muito feliz por ter escolhido o pREST para fazer sua primeira contribuição.

Fico muito feliz por ter escolhido o pREST para fazer sua primeira contribuição.

Realmente não existe uma fórmula mágica (how to) de como contribuir com projetos open source, vai de projeto para projeto, a dica que você deu de se envolver com as pessoas que mantêm e/ou contribui é ótima.

Parabéns pelo blogpost.

por Avelino em 31 de October de 2017 às 23:48

Python Help

Como autenticar um spider Scrapy em um website

Obs.: ainda não conhece o Scrapy? Então leia este tutorial.

Vez por outra os sistemas que a gente usa não entregam as informações da forma que desejamos. Seja o sistema do seu cartão de crédito que não lhe dá uma visualização legal dos seus gastos, ou até mesmo seu app de táxi que não lhe deixa fazer uma análise mais aprofundada dos trajetos que você tem feito.

Ah se todos eles tivessem um botãozinho “exportar tudo em JSON”! 🙂

A realidade é dura e a maioria dos apps não nos dão essa opção. Uma alternativa nesses casos é raspar os dados que desejamos do site do tal app, e isso envolve fazer seu web spider se autenticar no sistema. Nesse post, vamos ver como fazer para que um spider Scrapy faça login em um sistema qualquer.

Como funciona a autenticação em um website?

Existem várias maneiras diferentes, mas em linhas gerais a autenticação em websites funciona assim:

  1. O usuário acessa URL que contém formulário de login. Exemplo: http://quotes.toscrape.com/login
  2. O usuário preenche campos do formulário com suas credenciais e clica no botão de login
  3. O navegador envia os dados preenchidos para o servidor, no corpo de uma requisição HTTP do tipo POST
  4. O servidor valida as credenciais do usuário e envia de volta uma resposta contendo uma página indicando que o usuário se autenticou com sucesso e, juntamente com ela, um cookie de sessão.

O cookie de sessão é uma informação que o browser irá armazenar localmente e irá enviar juntamente com as requisições subsequentes ao mesmo website, de forma que o último possa identificar quem é o usuário fazendo tais requisições.

Assim sendo, nosso spider deverá reproduzir os passos recém descritos para que consiga se autenticar em um website. Então vamos a ele!

Nosso spider

Vamos construir um spider que se autentique em http://quotes.toscrape.com e de lá extraia o link para a página no Goodreads de cada autor, informação que só é visível para usuários autenticados.

Dê uma olhada no esqueleto do nosso spider:

# -*- coding:utf-8 -*-
import scrapy

class LoginSpider(scrapy.Spider):
    name = 'login-spider'
    start_urls = ['http://quotes.toscrape.com/login']

    def parse(self, response):
        # depois a gente vai implementar aqui o preenchimento do formulário
        # contido em response e fazer a requisição de login
        self.log('visitei a página de login: {}'.format(response.url))

    def parse_author_links(self, response):
        # aqui a gente vai extrair os links para as páginas dos autores
        # ou seja, quando chegar aqui, o spider já estará autenticado
        pass

Quando rodarmos nosso spider, o Scrapy vai chamar automaticamente o método parse() assim que a resposta para a URL http://quotes.toscrape.com/login tiver sido baixada e a mensagem de log será impressa. Vá em frente e rode o spider acima só pra ver isso acontecendo:

$ scrapy runspider loginspider.py

Manipulando o formulário

O formulário de login do nosso site está representado assim:





<form action="/login" method="post" accept-charset="utf-8" >
    <input type="hidden" name="csrf_token" value="fHQgXTCzxs...aOkolIudtjV"/>
    <label for="username">Username</label>
    <input type="text" class="form-control" id="username" name="username" />
    <label for="username">Password</label>
    <input type="password" class="form-control" id="password" name="password" />
    <input type="submit" value="Login" class="btn btn-primary" />
</form>




Perceba que temos 4 inputs nesse formulário: username, password, o botão de login em si e um campo oculto chamado csrf_token. Não vou entrar em detalhes sobre proteção CSRF, mas o que você precisa saber é que a maioria dos sites por aí vai ter um campo desses no formulário de login deles e que você precisa enviar tal campo juntamente com a requisição de autenticação. Caso contrário, o servidor não irá validar sua requisição.

No Scrapy, requisições do tipo POST podem ser feitas com um tipo especial de Request chamado FormRequest. Nossa requisição de autenticação será:

req = scrapy.FormRequest(
    url='http://quotes.toscrape.com/login',
    formdata={
        'username': 'john.doe',
        'password': 'anything',
        'csrf_token': token,
    },
    callback=self.parse_author_links,
}

Alguns detalhes importantes:

  • O valor do campo csrf_token é gerado dinamicamente para cada requisição à página de login. Assim, teremos que extrair ele da página de login antes de fazer a requisição.
  • A URL passada ao objeto FormRequest deve ser aquela encontrada no atributo action do formulário de login.
  • As chaves do dicionário passado ao parâmetro formdata devem usar como nome o valor do atributo name do input correspondente no formulário.

Agora que a gente já sabe como fazer uma requisição do tipo POST, vamos ao spider:

# -*- coding:utf-8 -*-
import scrapy
from scrapy.exceptions import CloseSpider

class LoginSpider(scrapy.Spider):
    name = 'login-spider'
    start_urls = ['http://quotes.toscrape.com/login']

    def parse(self, response):
        self.log('visitei a página de login: {}'.format(response.url))
        token = response.css('input[name="csrf_token"]::attr(value)').extract_first()
        yield scrapy.FormRequest(
            url='http://quotes.toscrape.com/login',
            formdata={
                'username': 'john.doe',
                'password': 'anything',
                'csrf_token': token,
            },
            callback=self.parse_author_links,
        )

    def parse_author_links(self, response):
        has_logout_link = response.css('a[href="/logout"]').extract_first()
        if not has_logout_link:
            raise CloseSpider('falha de autenticação')
        self.log('acabei de fazer login')

        links = response.css('.quote a[href*="goodreads.com"]::attr(href)').extract()
        for link in links:
            yield {'link': link}

        next_page = response.css('li.next a::attr(href)').extract_first()
        if next_page:
            yield scrapy.Request(
                url=response.urljoin(next_page),
                callback=self.parse_author_links,
            )

Na linha 11 (método parse()), extraímos o token da página para enviar junto com a requisição de autenticação e, logo em seguida, o método parse() gera a requisição POST para a URL de login. Repare que registramos o método self.parse_author_links como o callback para manipular a página enviada como resposta pelo servidor.

No método parse_author_links, verificamos se o botão de logout apareceu na página (o que indica que estamos autenticados) e, caso não esteja, encerramos a execução do spider (linha 25).

Depois disso, basta extrair as URLs dos autores (linhas 28-30). Veja a versão completa do spider aqui.

E o cookie de sessão?

Ah, lembra que falei no começo do artigo que o navegador armazena um cookie de sessão e envia ele junto com as requisições subsequentes pro mesmo site? Pois é, o Scrapy faz a mesma coisa pra gente. Ou seja, não precisamos nos preocupar em manter a sessão. 🙂

Simplificando um pouco

É bem comum termos que submeter valores presentes em campos ocultos (hidden) juntamente com os dados que preenchemos no formulário. Para facilitar a nossa vida, o Scrapy fornece um método chamado FormRequest.from_response(), que carrega automaticamente os valores dos campos do formulário que possuem valores default. Nosso método parse() ficaria assim se utilizássemos o from_response():

def parse(self, response):
    self.log('visitei a página de login: {}'.format(response.url))
    yield scrapy.FormRequest.from_response(
        response,
        formdata={
            'username': 'john.doe',
            'password': 'anything',
        },
        callback=self.parse_author_links,
    )

Como você pode ver, não precisamos extrair e passar o csrf token, nem passar a URL do formulário. O from_response já faz isso pra gente. 🙂

Por fim

Agora você já pode fazer um spider para extrair aqueles dados que você precisa para o seu relatório. Mas, antes de fazer seu spider, verifique se ele não está violando os termos de serviço do seu banco, ou seja lá qual sistema que você estiver acessando. 🙂

por Valdir Stumm Jr em 31 de October de 2017 às 00:01

October 08, 2017

Thiago Avelino

I would like to leave two complements:

I would like to leave two complements:

awesome-go: https://awesome-go.com

vim bootstrap: https://vim-bootstrap.com

por Avelino em 08 de October de 2017 às 10:32

September 10, 2017

Python Help

Esquisitices (ou não) no arredondamento em Python 3

O arredondamento de números em Python 3 pode ser feito usando uma função builtin chamada round(). Em sua forma mais simples, ela recebe um número qualquer e o arredonda para um número inteiro. Veja ela em ação:

>>> round(1.2)
1
>>> round(1.8)
2

Ela ainda aceita um segundo parâmetro, que indica quantos dígitos de precisão queremos no resultado. O valor padrão desse parâmetro é 0, mas podemos passar qualquer valor:

>>> round(1.847, ndigits=2)
1.85
>>> round(1.847, ndigits=1)
1.8

E o que será que acontece quando queremos arredondar um número como 1.5? A round() arredonda pra cima ou pra baixo? Vamos ver:

>>> round(1.5)
2

Arredonda pra cima? Vamos ver mais um número só pra ter certeza:

>>> round(2.5)
2

Ops, agora foi pra baixo! Vamos ver mais alguns:

>>> round(3.5)
4
>>> round(4.5)
4
>>> round(5.5)
6

Calma aí que tudo tem uma explicação. Em Python 3, a round() define o arredondamento assim:

Arredonda pro mais próximo.
Se empatar, arredonda pro número PAR mais próximo.

Agora faz sentido né? Revendo os exemplos dali de cima, vemos que o arredondamento sempre foi feito pro par mais próximo:

>>> round(3.5)
4
>>> round(4.5)
4
>>> round(5.5)
6

No caso do 3.5, tanto o 3 quanto o 4 estão à mesma distância dele. Ou seja, deu empate. Como a regra determina, round() desempata pro par mais próximo, que é 4. 🙂

E em Python 2?

Em Python 2 é diferente. O empate em arredondamentos é sempre resolvido pra cima, em caso de números positivos:

>>> round(1.5)
2.0
>>> round(2.5)
3.0

E pra baixo, em caso de números negativos:

>>> round(-1.5)
-2.0
>>> round(-2.5)
-3.0

Mas por que Python 3 faz diferente?

O objetivo é deixar o arredondamento menos tendencioso.

Imagine um banco onde todos os arredondamentos são feitos para cima. Ao final do dia, o relatório de receitas do banco vai mostrar um valor mais alto do que o banco realmente recebeu. É exatamente o que acontece em Python 2:

>>> valores = [1.5, 2.5, 3.5, 4.5]
>>> sum(valores)
12.0
>>> sum(round(v) for v in valores)
14.0

Usando a regra de arredondamento de Python 3, os valores arredondados nas operações tendem a ser amortizados, porque metade deles vai ser para cima e a outra metade para baixo, visto que metade dos números existente são pares e a outra metade são ímpares. Veja o mesmo código, agora rodando em Python 3:

>>> valores = [1.5, 2.5, 3.5, 4.5]
>>> sum(valores)
12.0
>>> sum(round(v) for v in valores)
12

Na realidade, isso não é uma novidade de Python 3. Esse tipo de arredondamento é antigo e tem até nome: Bankers Rounding (Arredondamento de Banqueiros).

Se ficou interessado no assunto, dê uma olhada num outro post daqui do blog, que mostra como funciona a divisão inteira em Python: https://pythonhelp.wordpress.com/2013/06/30/comportamento-inesperado-na-divisao-inteira/

por Valdir Stumm Jr em 10 de September de 2017 às 13:10

September 04, 2017

Thiago Avelino

JustForFunc by Francesc Campoy Golang core team…

JustForFunc by Francesc Campoy Golang core team https://www.youtube.com/channel/UC_BzFbxG2za3bp5NRRRXJSw

por Avelino em 04 de September de 2017 às 10:25

August 27, 2017

Humberto Rocha

Desbravando o pygame 4 - Game of Life

A um tempo atrás eu fiz uma postagem sobre como organizar um coding dojo publicada logo após um dojo de aniversário da comunidade python aqui do DF no qual eu preparei o desafio de implementar o jogo da vida (game of life) do matemático John Horton Conway.

Para deixar este desafio mais interativo preparei um simulador visual com pygame para testar nossa implementação. Este simulador utiliza de todos os conceitos que vimos até agora nesta série e aproveitando o interesse que ressurgiu no Grupy-DF sobre o assunto decidi reforçar os tópicos até então apresentados com a demonstração desta implementação.

O Jogo

O jogo da vida é um jogo sem jogadores, que representa a a dinâmica de interação entre um grupo de seres vivos em um plano bidimensional, partindo de uma configuração inicial e seguindo um conjunto de quatro regras simples.

Este plano possuí um formato tabular dividido em células, sendo que cada célula deste plano pode assumir dois estados, vivo ou morto. Além disso este plano tem dimensões infinitas, porém, limitaremos nosso plano a um recorte finito para simplificar nossa implementação.

A configuração inicial é chamada de semente (seed) e pode ser pré-definida ou aleatória sendo ela alterada a cada ciclo (geração) pelas regras de interação que são as seguintes:

  1. Toda célula com menos de dois vizinhos vivos morre por baixa população;

    regra um

  2. Toda célula com dois ou três vizinhos vivos sobrevive para a próxima geração;

    regra dois

  3. Toda célula com mais de três vizinhos vivos morre por superpopulação;

    regra tres

  4. Toda célula morta com exatamente três vizinhos vivos se torna uma célula viva por reprodução.

    regra quatro

Não há interação entre gerações, ou seja, se por exemplo uma célula nasce, ao se calcular a célula vizinha ela não é levada em conta pois ela não existia na geração vigente.

Implementação

O simulador foi feito para testar a lógica do jogo e se encontra no arquivo simulator.py enquanto no arquivo game.py fica o código responsável pela implementação do game of life. Sendo assim, analisaremos primeiramente o arquivo game.py, que não é o que implementamos no dia do dojo pois não concluímos toda a implementação a tempo, mas é uma versão que eu tinha criado previamente para testar o simulador e demonstrar ao final do dojo caso não chegássemos a implementação completa.

O desafio foi o seguinte, criar uma função game_of_life que recebesse uma seed em forma de uma matriz 50x50 e devolvesse a geração seguinte desta matriz como resultado:

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

def cell_check(section):
    '''
    Executa as regras do game of life em um recorte 3x3 para
    saber o estado da célula central
    '''
    # contador de vizinhos
    neighbours = 0
    # referência para o centro do recorte
    center = section[1][1]

    # somando todos os elementos do grupo
    for row in section:
        for cell in row:
            neighbours += cell

    # removendo o valor da célula central para que sobre somente
    # a soma dos vizinhos
    neighbours -= center

    # aplicando as regras do game of life
    # note que a regra dois não precisa ser ativamente aplicada, pois
    # ela não altera o estado da célula avaliada
    if neighbours <= 1:
        # menos de dois vizinhos a célula central morre por baixa população
        center = 0
    elif neighbours == 3:
        # exatamente três a célula nasce por reprodução
        center = 1
    elif neighbours >= 4:
        # mais que três a célula morre de super população
        center = 0

    # retorna o valor da célula central
    return center


def get_section(matrix, row, col):
    '''
    Extrai um recorte 3x3 em um plano tratando as extremidades do plano
    como células sempre mortas
    '''
    # monta um plano 3x3 somente com células mortas para fazer uma cópia
    # da área a ser analizada
    section = [[0 for _ in range(3)] for _ in range(3)]

    # percorre as redondezas da célula de posição row x col copiando seu
    # valor para section exceto quando ultrapassa a borda
    for sec_r, r in enumerate(range(row-1, row+2)):
        for sec_c, c in enumerate(range(col-1, col+2)):
            if r >= 0 and c >= 0 and r < 50 and c < 50:
                section[sec_r][sec_c] = matrix[r][c]

    # devolve o recorte 3x3 do plano
    return section


def game_of_life(seed):
    '''
    Recebe uma seed de um plano 50x50 executa o game of life e devolve
    a geração seguinte
    '''
    # cria um plano vazio para armazenar a nova geração pois não podemos
    # operar diretamente na geração corrente para não gerar efeito colateral
    next_gen = [[0 for _ in range(50)] for _ in range(50)]

    # percorre o plano tirando recortes 3x3 da vizinhança da célula central
    # e os avaliando para descobrir a geração seguinte de cada célula
    for r, row in enumerate(seed):
        for c, col in enumerate(row):
            next_gen[r][c] = cell_check(get_section(seed, r, c))

    # devolve a geração seguinte
    return next_gen

Como visto na implementação o código de game.py executa somente uma geração do game of life, o que é excelente para o nosso simulador.py, pois ele utilizará do nosso conceito de loop de jogo e a cada ciclo atualiza o jogo com a geração seguinte e desenha seu estado na tela:

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

import random
import time

import pygame

from game import game_of_life

# plano vazio
SEED = [[0 for _ in range(50)] for _ in range(50)]

# Glider
SEED[23][24] = 1
SEED[24][25] = 1
SEED[25][23] = SEED[25][24] = SEED[25][25] = 1

# Glider gun
# SEED[20][30] = 1
# SEED[21][28] = SEED[21][30] = 1
# SEED[22][18] = SEED[22][19] = SEED[22][26] = SEED[22][27] = SEED[22][40] = SEED[22][41] = 1
# SEED[23][17] = SEED[23][21] = SEED[23][26] = SEED[23][27] = SEED[23][40] = SEED[23][41] = 1
# SEED[24][6]  = SEED[24][7]  = SEED[24][16] = SEED[24][22] = SEED[24][26] = SEED[24][27] = 1
# SEED[25][6]  = SEED[25][7]  = SEED[25][16] = SEED[25][20] = SEED[25][22] = SEED[25][23] = SEED[25][28] = SEED[25][30] = 1
# SEED[26][16] = SEED[26][22] = SEED[26][30] = 1
# SEED[27][17] = SEED[27][21] = 1
# SEED[28][18] = SEED[28][19] = 1

# Rich's p16
# SEED[19][20] = SEED[19][21] = SEED[19][22] = SEED[19][26] = SEED[19][27] = SEED[19][28] = 1
# SEED[20][19] = SEED[20][23] = SEED[20][25] = SEED[20][29] = 1
# SEED[21][19] = SEED[21][23] = SEED[21][25] = SEED[21][29] = 1
# SEED[22][18] = SEED[22][20] = SEED[22][21] = SEED[22][22] = SEED[22][23] = SEED[22][25] = SEED[22][26] = SEED[22][27] = SEED[22][28] = SEED[22][30] = 1
# SEED[23][18] = SEED[23][19] = SEED[23][29] = SEED[23][30] = 1
# SEED[26][22] = SEED[26][23] = SEED[26][22] = SEED[26][25] = SEED[26][26] = 1
# SEED[27][21] = SEED[27][23] = SEED[27][25] = SEED[27][27] = 1
# SEED[28][22] = SEED[28][26] = 1

# Random
# SEED = [[random.choice([0, 1]) for _ in range(50)] for _ in range(50)]

pygame.init()

screen = pygame.display.set_mode((550, 550))


def draw_matrix(matrix):
    '''
    Função para desenhar o plano
    '''

    # preenche a tela de preto
    screen.fill([0, 0, 0])

    # percorre o plano desenhando as células com seus valores
    for r, row in enumerate(matrix):
        for c, cell in enumerate(row):
            if cell:
                # caso a célula esteja viva, a pinte de branco
                pygame.draw.rect(screen, (255, 255, 255),
                                 (11*c, 11*r, 10, 10))


# define a seed como um dos valores de exemplo do começo
seed = SEED

# desenha o estado inicial de nossa seed
draw_matrix(seed)

pygame.display.flip()

# espera por 1 segundo para podermos contemplar o estado de partida
time.sleep(1)

while True:
    # PROCESSAMENTO DE ENTRADA
    event = pygame.event.poll()

    if event.type == pygame.QUIT:
        # finaliza o programa caso clique em fechar
        break
    elif (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
        # finaliza o programa caso pressione a tecla ESC
        break

    # ATUALIZAÇÃO DO JOGO

    # aplica o game of life para processar a geração seguinte
    seed = game_of_life(seed)

    # DESENHO

    # desenha a nova geração na tela
    draw_matrix(seed)

    pygame.display.flip()

    # espera um breve momento antes de partir para o próximo ciclo
    time.sleep(0.05)

Desenhamos uma vez a seed na tela antes de iniciar o loop para dar tempo do usuário ver o estado inicial na tela. Em seguida damos início ao loop de jogo checando por eventos como feito anteriormente mas com a adição da condição de parada utilizando a tecla ESC para finalizar o programa. Na etapa de atualização do jogo rodamos a função game_of_life do arquivo game.py para processar a geração seguinte e finalizamos com a etapa de desenho, agora com um tempo de espera menor entre os ciclos para ter uma visão mais dinâmica da evolução da nossa seed.

Exemplos

Para demonstrar o simulador em funcionamento utilizei o glider que é o padrão mais famoso dentro do game of life e foi absorvido pela comunidade hacker como seu emblema. Ele é o menor padrão cíclico que realiza deslocamento pelo, plano:

glider

Obs.: Note que existem alguns padrões comentados no código do simulador, você pode remover o comentário de qualquer um deles para ver padrões diferentes em ação.

Conclusão

O game of life é um conceito muito bom para se aprender conceitos da computação, recomendo assistir as entrevistas com seu criador pelo canal Numberphile no youtube.

E como sempre o código está disponível no repositório para todos.

por Humberto Rocha em 27 de August de 2017 às 00:00

August 24, 2017

Bruno Cezar Rocha

Simple Login Extension for Flask

Travis PyPI PyPI PyPI Flask

Login Extension for Flask

There are good and recommended options to deal with web authentication in Flask.

I recommend you use:

Those extensions are really complete and production ready!

So why Flask Simple Login?

However sometimes you need something simple for that small project or for prototyping.

Flask Simple Login

What it provides:

  • Login and Logout forms and pages
  • Function to check if user is logged-in
  • Decorator for views
  • Easy and customizable login_checker

What it does not provide: (but of course you can easily implement by your own)

  • Database Integration
  • Password management
  • API authentication
  • Role or user based access control

Hot it works

First install it from PyPI.

pip install flask_simplelogin

from flask import Flask
from flask_simplelogin import SimpleLogin

app = Flask(__name__)
SimpleLogin(app)

That's it! now you have /login and /logout routes in your application.

The username defaults to admin and the password defaults to secret (yeah that's not clever, let's see how to change it)

Configuring

Simple way

from flask import Flask
from flask_simplelogin import SimpleLogin

app = Flask(__name__)
app.config['SECRET_KEY'] = 'something-secret'
app.config['SIMPLELOGIN_USERNAME'] = 'chuck'
app.config['SIMPLELOGIN_PASSWORD'] = 'norris'

SimpleLogin(app)

That works, but is not so clever, lets use env vars.

$ export SIMPLELOGIN_USERNAME=chuck
$ export SIMPLELOGIN_PASSWORD=norris

then SimpleLogin will read those env vars automatically.

from flask import Flask
from flask_simplelogin import SimpleLogin

app = Flask(__name__)
app.config['SECRET_KEY'] = 'something-secret'
SimpleLogin(app)

But what if you have more users and more complex auth logic? write a custom login checker

Using a custom login checker

from flask import Flask
from flask_simplelogin import SimpleLogin

app = Flask(__name__)
app.config['SECRET_KEY'] = 'something-secret'


def only_chuck_norris_can_login(user):
    "user = {'username': 'foo', 'password': 'bar'}"
    # do the authentication here, it is up to you!
    # query your database, check your user/passwd file
    # connect to external service.. anything.
    if user.get('username') == 'chuck' and user.get('password') == 'norris':
       return True  # Allowed
    return False  # Denied


SimpleLogin(app, login_checker=only_chuck_norris_can_login)

Checking if user is logged in


from flask_simplelogin import is_logged_in

if is_logged_in():
    # do things if anyone is logged in

if is_logged_in('admin'):
    # do things only if admin is logged in

Decorating your views

from flask_simplelogin import login_required

@app.route('/it_is_protected')
@login_required   # < --- simple decorator
def foo():
    return 'secret'

Protecting Flask Admin views


from flask_admin.contrib.foo import ModelView
from flask_simplelogin import is_logged_in


class AdminView(ModelView)
    def is_accessible(self):
        return is_logged_in('admin')

Customizing templates

There are only one template to customize and it is called login.html

Example is:

{% extends 'base.html' %}
{% block title %}Login{% endblock %}
{% block messages %}
   {{super()}}
   {%if form.errors %}
     <ul class="alert alert-danger">
       {% for field, errors in form.errors.items() %}
         <li>{{field}} {% for error in errors %}{{ error }}{% endfor %}</li>
       {% endfor %}
     </ul>
   {% endif %}
{% endblock %}

{% block page_body %}
       <form action="{{ url_for('simplelogin.login', next=request.args.get('next', '/')) }}" method="post">
            <div class="form-group">
            {{ form.csrf_token }}
            {{form.username.label}}<div class="form-control">{{ form.username }}</div><br>
            {{form.password.label}}<div class="form-control"> {{ form.password }}</div><br>
            </form>
           <input type="submit" value="Send">
       </form>
{% endblock %}

Take a look at the example app.

And you can customize it in anyway you want and need, it receives a form in context and it is a WTF form the submit should be done to request.path which is the same /login view.

You can also use {% if is_logged_in %} in your template if needed.

Requirements

  • Flask-WTF and WTForms
  • having a SECRET_KEY set in your app.config

por Bruno Rocha em 24 de August de 2017 às 11:03

Humberto Rocha

Desbravando o pygame 3 - Game Loop

Agora que sabemos como desenhar na tela (postagem anterior) seria interessante que nosso jogo ficasse rodando até que alguém o feche. Para isso vamos utilizar um dos fundamentos do desenvolvimento de jogos que é o Game Loop.

Conceito

Dentro da computação o conceito de ciclo é bastante recorrente, trata-se de uma sequência de ações e tomadas de decisão programadas que se repetem dentro de um laço de repetição (ou loop). No baixo nível temos o processador do computador que trabalha em ciclos de operações aritméticas gerenciadas pelo clock, em sistemas operacionais o ciclo é quem gerencia o tempo de uso do processador, o dividindo entre todas as aplicações do sistema além de verificar se o usuário realizou algum comando, em servidores web usamos um ciclo para verificar se alguém requisitou uma página que será processada e devolvida em forma de resposta. Com jogos não seria diferente.

O ciclo dentro do desenvolvimento de jogos é chamado de Game Loop e é peça fundamental na estrutura de um jogo. De modo simplificado temos a seguinte estrutura:

loop basico

Durante a execução jogo está sempre:

  • Recebendo e processando entradas do usuário que são detectados por eventos gerados por dispositivos de entrada como um teclado e um mouse.

  • Atualizando o jogo de acordo com os eventos processados e de outras mecânicas como aplicação de gravidade, detecção de colisão, eventos programados dentre outros.

  • Desenhando na tela o resultado de toda a interação anterior.

Diferente de um loop de eventos de uma aplicação de desktop por exemplo em que o programa fica parado até que você tome alguma ação, no loop de jogo continua sua execução independente da entrada do usuário atualizando elementos independentes como, música, física, inteligência artificial, etc.

Um outro ponto importante que não está nos itens acima mas está presente na grande maioria dos jogos é o controle de atualização do loop. Mas este tópico ficará para uma próxima postagem.

Implementação

Para demonstrar o game loop em funcionamento faremos um programa que desenha uma bola que quica pelos cantos da tela:

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

import pygame

# definindo cores
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

pygame.init()

screen = pygame.display.set_mode((640, 480))

pygame.display.set_caption('Game Loop')

# variáveis da bola
position_x = 300
position_y = 200
velocity_x = 1
velocity_y = 1

# iniciando o loop de jogo
while True:
    # PROCESSAMENTO DE ENTRADA

    # capturando eventos
    event = pygame.event.poll()
    # caso o evento QUIT (clicar no x da janela) seja disparado
    if event.type == pygame.QUIT:
        # saia do loop finalizando o programa
        break

    # ATUALIZAÇÃO DO JOGO

    # movendo a bola
    position_x += velocity_x
    position_y += velocity_y

    # mudando a direção no eixo x nas bordas
    if position_x > 600:
        velocity_x = -1
    elif position_x < 0:
        velocity_x = 1

    # mudando a direção no eixo y nas bordas
    if position_y > 440:
        velocity_y = -1
    elif position_y < 0:
        velocity_y = 1

    # DESENHO

    # preenchendo o fundo com preto
    screen.fill(BLACK)

    # desenhando a bola
    pygame.draw.ellipse(screen, RED, [position_x, position_y, 40, 40])

    # atualizando a tela
    pygame.display.flip()

É assim, bem direto ao ponto, o loop definido em while True: irá rodar o código indefinidamente realizando as etapas de processamento de entrada, atualização do jogo e desenho a cada ciclo que passa.

Na etapa de processamento de entrada usamos pygame.event.poll() para recuperar um evento da fila e checar se ele é um evento de término de programa. Se ele for, usamos break para sair do loop, finalizando assim o programa.

Durante a etapa de atualização do jogo movemos a nossa bola no eixo x e y verificando se a bola se encontra nas extremidades e alterando sua direção em caso positivo.

E finalmente na etapa de desenho usamos as funções previamente apresentadas para mostrar nossa bola na tela.

Note que sempre pintamos o fundo de preto, caso contrário o rastro da bola ficaria na tela.

Concluindo

E com isso finalizamos nosso breve passeio pelo game loop. Não se esqueça que todo código estará disponível no repositório desbravando-pygame.

por Humberto Rocha em 24 de August de 2017 às 00:00

August 23, 2017

Bruno Cezar Rocha

Publish your Python packages easily using flit

Deploying Python Packages to PyPI using Flit

The traditional way of package deployment in Python is using a setup.py script located in the root of your project and then running python setup.py sdist upload to release a new version.

It works by using distutils, setuptools or distribute and there is also twine which is a command line application to manage uploads.

History

distutils is the standard way of Python package distribution included in standard library since Python 2.x then setuptools was created to overcome distutils limitations and also introduces a command line application called easy_install (currently we use its sucessor called pip) and also setuptools introduced a very handy feature called pkg_resources. One of the characteristics of setuptools is that it uses Monkey Patching over the standard distutils to fix existing problems.

Other forks of setuptools has been created to fix that issues and add common developers preferences so well known forks like distribute and distutils2 and distlib has been merged back to the original setuptools

Lots of other packaging tools has been created to try to fix the distribution problems, some maintained by PyPA (Python Package Authority) and some maintained by community.

How it works in standard way

using one of the above you should create a file called setup.py in the root of your project, e.g:

from <my_favorite_dist_tool> import setup

# Example taken from Django's repository
setup(
    name='Django',
    version=version,
    url='https://www.djangoproject.com/',
    author='Django Software Foundation',
    author_email='foundation@djangoproject.com',
    description=('A high-level Python Web framework that encourages '
                 'rapid development and clean, pragmatic design.'),
    license='BSD',
    packages=find_packages(exclude=EXCLUDE_FROM_PACKAGES),
    include_package_data=True,
    scripts=['django/bin/django-admin.py'],
    entry_points={'console_scripts': [
        'django-admin = django.core.management:execute_from_command_line',
    ]},
    install_requires=['pytz'],
    extras_require={
        "bcrypt": ["bcrypt"],
        "argon2": ["argon2-cffi >= 16.1.0"],
    },
    zip_safe=False,
    classifiers=[
        'Development Status :: 2 - Pre-Alpha',
        'Environment :: Web Environment',
        'Framework :: Django',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: BSD License',
        'Operating System :: OS Independent',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Topic :: Internet :: WWW/HTTP',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
        'Topic :: Internet :: WWW/HTTP :: WSGI',
        'Topic :: Software Development :: Libraries :: Application Frameworks',
        'Topic :: Software Development :: Libraries :: Python Modules',
    ],
)

As you can see it is very confusing to decide which of the distribute tools to addopt and how the setup.py should be writen because there are different examples over the github most famous Python repositories.

Making things easier with Flit

NOTE: Forget all about all that history and setup.py you read above and consider using only Flit.

Flit is a simple way to Package and deploy Python projects on PyPI, Flit makes it easier by using a simple flit.ini file and assumes common defaults to save your time and typing.

I knew about Flit when I was taking a look at Mariatta Wijaya game called Tic Tac Taco Pizza and noticed that she used flit to deploy the game, so we also asked her the reason for using this on the podcast we recorded so I decided to try porting my projects to Flit.

How it works?

Instead of a complex setup.py you put a simple flit.ini in the root of your project and it looks like:

[metadata]
module = markdocs
author = Bruno Rocha
author-email = rochacbruno@gmail.com
maintainer = rochacbruno
maintainer-email = rochacbruno@gmail.com
home-page = https://github.com/rochacbruno/markdocs
requires = mistune
           click
description-file = README.md
classifiers = Programming Language :: Python :: 3.6
              Intended Audience :: Developers
              License :: OSI Approved :: MIT License
              Topic :: Documentation
              Topic :: Software Development :: Documentation
              Topic :: Software Development :: Quality Assurance
requires-python = >=3.6

[scripts]
markdocs = markdocs:main

Now you only need to have flit installed in your local machine pip3 install flit (unsing pip3 as flit works only in Python3+) and optionally is recommended to have pandoc and pypandoc installed because Flit can convert your README.md into the .rst, the format still used by PyPI (note that Markdown support is coming to PyPi.org soon).

The advantages are:

  • No more complicated setup.py
  • If you omit some fields it will assume common used defaults
  • It is easier to read and write
  • Will convert your README.md
  • Will take the __version__ included in your program/__init__.py
  • Can deploy to TestPyPI
  • Avoids over engineering on setup.py

Development installation

To install your package during development use

flit install --symlink --python path/to/virtualenv/bin/python

the --python is optional, by default will take the current which python

Registering and deploying

It is easy and will register the new project for you if doesn't exist on PyPI

flit publish

Flit packages a single importable module or package at a time, using the import name as the name on PyPI. All subpackages and data files within a package are included automatically.

Important!

  • Flit will use the data from ~/.pypirc to authenticate and to find the server deployment addresses
  • You can also set FLIT_USERNAME and FLIT_PASSWORD and FLIT_INDEX_URL as environment variables which makes flit good for CI deployment (e.g: TravisCI)

What is missing?

NOTE: Flit is open-source, so some of this things are already under consideration and there are PRs opened.

  • Flit will not bump your project version automatically, you can still use tools like bumpversion but this feature would be better if builtin
  • Flit will not parse a requirements.txt file and would be nice to have it as tools like pyup.io can track those files but not flit.ini yet
  • Flit does not create a .lock frozen file with the version used on specific release and it is interesting just like pipenv does

Conclusion

Flit is the easier way to deploy packaged to PyPI following 3 simple steps.

  1. Install flit
  2. Describe your flit.ini
  3. Run flit publish

then your library is released to PyPI.

However

Python still needs better standards because you still need separated tools to make common tasks and using a single tool to that tasks (pack, install, deploy, create) would be better (just like what cargo does for Rust), instead in Python you have:

  • Flit to deploy packages to PyPI for distribution
  • bumpversion to bump your semver number
  • pip to install and update packages from PyPI (or pipenv/WIP to do the same with more powers)
  • Cookiecutter to create a new Python Package (from strucured templates)
  • safety to check dependencies security
  • flake or pylint to static and styling checks
  • venv, pyenv or virtualenvwrapper to manage isolated environments
  • pytest to run tests
  • pyup.io to watch for depdendency updates
  • tox for testing on multiple environments
  • sphinx to create documentation
    • lots of other options

Having so many tools brings a lot of confusion and makes it hard to choose, why not having a single tool, based on different plugins sharing the same API?

Just an idea

python -m manage [install packagename]               # <-- calls pip or pipenv
                 [publish --options --bump=version]  # <-- calls `flit` and `bumpversion`
                 [new packagename templatename]      # <-- calls cookiecutter
                 [safecheck]                         # <-- calls safety
                 [checkupdates]                      # <-- checks in the same way as Pyup.io does
                 [test path]                         # <-- calls pytest, nose or unittest
                 [lint path]                         # <-- calls flake, pylint 
                 [venv options]                      # <-- calls the existing venv module
                 [docs --options]                    # <-- calls sphinx, pydoc, markdocs or other

All above configurable via config file or env vars and each of that endpoints would be provided by many plugins sharing the same API, so you could choose between flit or twine as your publish manager etc..

So maybe I can implement that features in manage

Please share in comments if you know some other Python management tool

por Bruno Rocha em 23 de August de 2017 às 11:23

August 20, 2017

Thiago Avelino

August 19, 2017

Blog do PauloHRPinheiro

Listas, referências e cópias em Python

Um erro bem comum (pelo menos pra mim), lidando com listas em Python

19 de August de 2017 às 00:00

July 31, 2017

Flavio Ribeiro

Using Open Source to Create a Video Thumbnail Service

Using Open Source to Create a Video Thumbnail Service

Last week The New York Times hosted the 2017’s edition of Makers Week, an entire week dedicated to working on projects and ideas employees want to test, build and innovate on.

There are no boundaries to projects, nor specific scope requirements. You can use your time to do research on new topics or disciplines, contribute to open source projects, fix bugs or create products from scratch. It's definitely not a new thing and I believe most companies are doing this now so I will not dwell on it. If you want to know more about how this works here at our office, you can read this article from last year’s edition.

Increasing our click through rate

One of our goals as a video team this year is to increase our click through rate when our users see our video player. We usually do a pretty good job of selecting thumbnails for all of our videos — often using photos taken by our own photo journalists. However, after watching a talk from JWPlayer folks and reading a research article from Netflix, it became clear to me that we can't just assume a thumbnail is good for a given video. We should actually use some data and run A/B tests to come up with the best one.

In order to try to improve our thumbs, we'd need to be able to create them in a cleverer and faster way. So I thought that creating something to generate and serve thumbnails for any of our videos at any time, on-the-fly (on the time of an HTTP request), would be a great project for my Makers Week. When working at Globo.com, I saw some amazing open source projects being created and maintained by some wonderful engineers I met over there so the scope of the project would be nothing more than putting some of those projects together.

Lumberjack

Before starting, I invited Francisco Souza to help me. He's a specialist when it comes to application deployment and all things related to Docker/Kubernetes/Google Cloud Service. I'm glad he accepted, things were much easier with his help.

Using Open Source to Create a Video Thumbnail Service

As I said before, Lumberjack is a combination of open source projects that allow thumbnails extraction for videos. It leverages the powers of Thumbor, NGINX, nginx-thumbextractor module (or simply módulo do Wanden as we like to say in Portuguese) and the Lua programming language in order to extract and generate pictures for any given moment of any given video of our video library.

Architecture

We are deploying three services in different containers in a Kubernetes pod:

  • NGINX + Video Thumbnails Extractor Module: Responsible for scanning the video on a given mountpoint, extracting the frame of a given timecode and returning it on the fly. We used gcsfuse to mount our production GCS bucket where our videos library reside, allowing the module to go there and get the frame requested.

  • Thumbor: Responsible for applying filters, crops and resizing of images. Thumbor relies on different engines including OpenCV to apply functions passed as query strings on the HTTP request. It includes smart cropping, face and assets detections and a bunch of other cool stuff. You should take a look on this powerful project and see what it can do for you. The Docker image we are using for this service is available here.

  • NGINX + Lua Application: A simple Lua script that runs inside NGINX with this Lua Module. Given a VideoID, the script is able to fetch the right MP4 video asset from our internal API and send to the thumbnails extractor. The application is also responsible for getting the parameters passed from the user on the URL such as filters and resolution, send to Thumbor and return the final picture back to the user.

Using Open Source to Create a Video Thumbnail Service

We decided not to open source Lumberjack. At the end of the day the whole project is just some business logics around the open source projects I mentioned on this post. So really all of the credit belongs to the guys who created and maintain Thumbor and the thumbextractor module. If you want something similar to what we did you can just deploy them.

Some Use Cases

Imagine we want to crop a frame from this video at 30 seconds to use as the cover of another vertical video on mobile phones (9x16), in grayscale, with smart cropping. We just need to pass the below as parameters and the service will make it for you:

lumberjack.nyt.net/video-id?timecode=30&filters=grayscale()&resolution=900x1600&smartcrop=true

Using Open Source to Create a Video Thumbnail Service

Another great feature supported by the thumbnails extractor is the generation of sprites or tiles. We can, for example, generate a sprite map with thumbnails every one second and use it as a moving cover when the user hovers on it:

lumberjack.nyt.net/video-id?sprite=true&size=100&interval=1

Using Open Source to Create a Video Thumbnail Service

See how it looks by placing the pointer over the image below:

We can also use the similar approach to use the sprite map for the thumbnails on the seekbar as you can see on this player:

Future

The Thumbnails Service is now part of our Q4 roadmap and Lumberjack is already in production. We didn't present to the newsroom yet as we are still facing some performance issues with caching and the GCS bucket. The NGINX locations are not optimized so we'll need to look back on them as well. To sum up, we'll need to revisit a lot of stuff that we did in a rush during Makers week (I don't even need to mention that we have zero tests for the Lua script too).

For the future, we want to be able to detect perfect looping GIF's for using them on social channels and also detect highlights of a video based on the audio and closed captions. That way we can suggest thumbnails for our newsroom editors within our CMS. Finally, we'd love to integrate thumbnails tests with our A/B framework the same way Netflix did.

por Flávio Ribeiro em 31 de July de 2017 às 21:20

July 23, 2017

Blog do PauloHRPinheiro

July 21, 2017

PythonClub

Peewee - Um ORM Python minimalista

Peewee é um ORM destinado a criar e gerenciar tabelas de banco de dados relacionais através de objetos Python. Segundo a wikipedia, um ORM é:

Mapeamento objeto-relacional (ou ORM, do inglês: Object-relational mapping) é uma técnica de desenvolvimento utilizada para reduzir a impedância da programação orientada aos objetos utilizando bancos de dados relacionais. As tabelas do banco de dados são representadas através de classes e os registros de cada tabela são representados como instâncias das classes correspondentes.

O que o ORM faz é, basicamente, transformar classes Python em tabelas no banco de dados, além de permitir construir querys usando diretamente objetos Python ao invés de SQL.

O Peewee é destinado a projetos de pequeno/médio porte, se destacando pela simplicidade quando comparado a outros ORM mais conhecidos, como o SQLAlchemy. Uma analogia utilizada pelo autor da API e que acho muito interessante é que Peewee está para o SQLAlchemy assim como SQLite está para o PostgreSQL.

Em relação aos recursos por ele oferecidos, podemos citar que ele possui suporte nativo a SQLite, PostgreSQL e MySQL, embora seja necessário a instalação de drivers para utilizá-lo com PostgreSQL e MySQL e suporta tanto Python 2.6+ quanto Python 3.4+.

Neste tutorial, utilizaremos o SQLite, por sua simplicidade de uso e por não precisar de nenhuma configuração.

Instalação

O Peewee pode ser facilmente instalado com o gerenciador de pacotes pip:

pip install peewee

Criando o banco de dados

Para criar as tabelas é bem simples. Inicialmente passamos o nome do nosso banco de dados (a extensão *.db indica um arquivo do SQLite).

import peewee

db = peewee.SqliteDatabase('codigo_avulso.db')

Diferente de outros bancos de dados que funcionam através um servidor, o SQLite cria um arquivo de extensão *.db, onde todos os nossos dados são armazenados.

DICA: caso deseje ver as tabelas existentes no arquivo codigo_avulso.db, instale o aplicativo SQLiteBrowser. Com ele fica fácil monitorar as tabelas criadas e acompanhar o tutorial.

 sudo apt-get install sqlitebrowser

A título de exemplo, vamos criar um banco destinado a armazenar nomes de livros e de seus respectivos autores. Comecemos primeiro com a classe que representa os autores.

import peewee

db = peewee.SqliteDatabase('codigo_avulso.db')

class Author(peewee.Model):
    """
    Classe que representa a tabela Author
    """

    # A tabela possui apenas o campo 'name', que
    # receberá o nome do autor
    name = peewee.CharField()

    class Meta:
        # Indica em qual banco de dados a tabela
        # 'author' sera criada (obrigatorio). Neste caso,
        # utilizamos o banco 'codigo_avulso.db' criado anteriormente.
        database = db

Em seguida, criamos a classe que representa os livros. Ela possui uma relação de "muitos para um" com a tabela de autores, ou seja, cada livro possui apenas um autor, mas um autor pode possuir vários livros.

import peewee

db = peewee.SqliteDatabase('codigo_avulso.db')

class Book(peewee.Model):
    """
    Classe que representa a tabela Book
    """

    # A tabela possui apenas o campo 'title', que
    # receberá o nome do livro
    title = peewee.CharField()

    # Chave estrangeira para a tabela Author
    author = peewee.ForeignKeyField(Author)

    class Meta:
        # Indica em qual banco de dados a tabela
        # 'author' sera criada (obrigatorio). Neste caso,
        # utilizamos o banco 'codigo_avulso.db' criado anteriormente.
        database = db

Agora, vamos reunir tudo em um único arquivo model.py. Como exemplo, eu criei um arquivo main.py para utilizarmos as classes que acabamos de criar.

import peewee
from model import Author, Book


if __name__ == '__main__':
    try:
        Author.create_table()
    except peewee.OperationalError:
        print 'Tabela Author ja existe!'

    try:
        Book.create_table()
    except peewee.OperationalError:
        print 'Tabela Book ja existe!'

Após executarmos o código, será criado um arquivo de nome codigo_avulso.db no mesmo diretório do nosso arquivo main.py, contendo as tabelas Author e Book. A estrutura do diretório ficou assim:

.
├── codigo_avulso.db
├── main.py
├── model.py

Inserindo dados no banco

Agora, vamos popular nosso banco com alguns autores e seus respectivos livros. Isso pode ser feito de dois modos. Através do método create, quando desejamos inserir um registro apenas; ou pelo método insert_many, quando desejamos inserir vários registros de uma vez em uma mesma tabela.

# Inserimos um autor de nome "H. G. Wells" na tabela 'Author'
author_1 = Author.create(name='H. G. Wells')

book_1 = {
    'title': 'A Máquina do Tempo',
    'author': author_1,
}

book_2 = {
    'title': 'Guerra dos Mundos',
    'author': author_1,
}

# Inserimos um autor de nome "Julio Verne" na tabela 'Author'
author_2 = Author.create(name='Julio Verne')

book_3 = {
    'title': 'Volta ao Mundo em 80 Dias',
    'author': author_2,
}

book_4 = {
    'title': 'Vinte Mil Leguas Submarinas',
    'author_id': author_1,
}

books = [book_1, book_2, book_3, book_4]

# Inserimos os quatro livros na tabela 'Book'
Book.insert_many(books).execute()

Consultando dados no banco

O Peewee possui comandos destinados a realizar consultas no banco. De maneira semelhante ao conhecido SELECT. Podemos fazer essa consulta de duas maneiras. Se desejamos o primeiro registro que corresponda a nossa pesquisa, podemos utilizar o método get().

book = Book.get(Book.title == "Volta ao Mundo em 80 Dias").get()
book.title

Porém, se desejamos mais de um registro, utilizamos o método select. Por exemplo, para consultar todos os livros escritos pelo autor "H. G. Wells".

books = Book.select().join(Author).where(Author.name=='H. G. Wells')

# Exibe a quantidade de registros que corresponde a nossa pesquisa
print books.count()

for book in books:
    book.title

# Resultado:
# * A Máquina do Tempo
# * Guerra dos Mundos
# * Vinte Mil Leguas Submarinas

Também podemos utilizar outras comandos do SQL como limit e group (para mais detalhes, ver a documentação aqui).

Alterando dados no banco

Alterar dados também é bem simples. No exemplo anterior, se observarmos o resultado da consulta dos livros do autor "H. G. Wells", iremos nos deparar com o livro de título "Vinte Mil Léguas Submarinas". Se você, caro leitor, gosta de contos de ficção-científica, sabe que esta obra foi escrito por "Julio Verne", coincidentemente um dos autores que também estão cadastrados em nosso banco. Sendo assim, vamos corrigir o autor do respectivo livro.

Primeiro vamos buscar o registro do autor e do livro:

new_author = Author.get(Author.name == 'Julio Verne')
book = Book.get(Book.title=="Vinte Mil Leguas Submarinas")

Agora vamos alterar o autor e gravar essa alteração no banco.

# Alteramos o autor do livro
book.author = new_author

# Salvamos a alteração no banco
book.save()

Deletando dados do banco

Assim como as operações anteriores, também podemos deletar registros do banco de maneira bem prática. Como exemplo, vamos deletar o livro "Guerra dos Mundos" do nosso banco de dados.

# Buscamos o livro que desejamos excluir do banco
book = Book.get(Book.title=="Guerra dos Mundos")

# Excluimos o livro do banco
book.delete_instance()

Simples não?

Conclusão

É isso pessoal. Este tutorial foi uma introdução bem enxuta sobre o Peewee. Ainda existem muitos tópicos que não abordei aqui, como a criação de primary_key, de campos many2many entre outros recursos, pois foge do escopo deste tutorial. Se você gostou do ORM, aconselho a dar uma olhada também na sua documentação, para conseguir extrair todo o potencial da ferramenta. A utilização de um ORM evita que o desenvolvedor perca tempo escrevendo query SQL e foque totalmente no desenolvimento de código.

O Peewee também possui suporte ao flamework flask, então dependendo do tamanho do projeto, pode ser uma alternativa interessante no lugar de ORM mais complexos como o SQLAlchemy.

É isso pessoal. Obrigado pela leitura e até o próximo tutorial!

Referências

por Michell Stuttgart em 21 de July de 2017 às 02:45

July 10, 2017

Rodrigo Delduca

Criando um bot de notícias para o Telegram usando Scrapy e Firebase

Problema

Eu costumo pegar com frequência a rodovia Régis Bittencourt e o que acontece com frequência é o trânsito parar completamente no meio do nada e sem acesso à internet, então eu fico sem a mínima noção do que está acontecendo e em quanto tempo conseguirei chegar ao meu destino.

Pensando nisso, decidi escrever um pequeno bot para o Telegram que publica num canal as notícias da estrada! Como de costume no NULL on error, vou explicar como fiz.

Web scraping

Squitter

O primeiro passo é extrair as informações do site. Eu optei por utilizar o framework Scrapy, por alguns motivos que ficarão bem claros abaixo e por ter bastante experiência escrevendo web crawlers com o Scrapy - eu não pretendo escrever um tutorial a respeito neste artigo, isso ficará para uma próxima oportunidade.

Antes de tudo, eu preciso definir o que eu quero extrair; isso é feito definindo uma classe com N propriedades herdando de scrapy.Item

class Entry(Item):
    uid = Field()
    spider = Field()
    timestamp = Field()
    content = Field()

Como é possível notar, a aranha, ou crawler, ficou bem simples, mas vou explicar cada parte a seguir.

class RegisSpider(CrawlSpider):
    name = 'regis'
    allowed_domains = ['autopistaregis.com.br']
    start_urls = ['http://www.autopistaregis.com.br/?link=noticias.todas']
    rules = (
        Rule(LinkExtractor(allow=r'\?link=noticias.?ver*'), callback='parse_news'),
    )

    def parse_news(self, response):
        loader = EntryLoader(item=Entry(), response=response)
        loader.add_xpath('content', '//*[@id="noticia"]/p[not(position() > last() -3)]//text()')
        return loader.load_item()

A propriedade start_urls indica onde a aranha vai iniciar a varredura de páginas

Após isso, definimos algumas regras. Vou usar um LinkExtractor, que, como o próprio nome diz é um componente para extrair links seguindo uma regra das páginas encontradas. Nesse caso eu usei uma expressão regular que bate com todas as URLS de notícias do site, e defino um callback que será chamado para cada página, chamado parse_news.

LinkExtractor(allow=r'\?link=noticias.?ver*'), callback='parse_news')

Então é aqui que a mágica toda acontece: passei algum tempo analisando o código fonte da página e usando o inspetor web para poder gerar um xpath que bata com notícia, excluindo as informações extras na página.

XPath

O XPath é uma forma de atravessar o HTML e extrair alguma informação específica. É uma linguagem bem poderosa. Nesse caso eu usei a expressão [not(position() > last() -3)] para excluir os últimos 3 parágrafos marcados pela tag <p>, que o site sempre coloca como uma forma de rodapé. Infelizmente, nem sempre os sites seguem boas práticas, o que me facilitaria e muito a extração dos dados!

loader.add_xpath('content', '//*[@id="noticia"]/p[not(position() > last() -3)]//text()')

Os outros campos, como ID da noticía e timestamp são “extraídos” usando um middleware chamado scrapy-magicfields, desta maneira:

MAGIC_FIELDS = {
    'uid': "$response:url,r'id=(\d+)'",
    'spider': '$spider:name',
    'timestamp': "$time",
}

O próximo passo é rodar o web crawler periodicamente. Eu usei o sistema de cloud do Scrapinghub, que é a empresa que desenvolve o Scrapy e outras ferramentas de scraping; nele, eu posso configurar para rodar de tempos em tempos o crawler. No meu caso, eu configurei para rodar a cada 30 minutos,

Mesmo que possível, eu não posso publicar diretamente, apenas as novas notícias, caso contrário, toda vez que o crawler rodar eu estaria poluindo o canal com as notícias repetidas. Então eu decidi salvar num banco de dados intermediário para conseguir distinguir o que é novo do que já foi indexado.

Persistência

Eis que entra o Firebase, e sua nova funcionalidade chamada de functions, com o qual, eu posso escrever uma função que reage a determinados eventos no banco de dados - por exemplo, quando um novo dado é inserido.

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const request = require('request-promise');
const buildUrl = require('build-url');

admin.initializeApp(functions.config().firebase);


exports.notifyChannel = functions.database.ref('/news/{what}/{uid}')
  .onCreate(event => {
    const config = functions.config().telegram;
    const url = buildUrl('https://api.telegram.org', {
      path: `bot${config.bot.token}/sendMessage`,
      queryParams: {
        chat_id: config.channel.chat_id,
      }
    });

    return request({
      method: 'POST',
      uri: url,
      resolveWithFullResponse: true,
      body: {
        text: event.data.val().content
      },
      json: true
    }).then(response => {
      if (response.statusCode === 200) {
        return response.body.id;
      }
      throw response.body;
    }
  );
});

Essa função é bem simples; basicamente, em qualquer evento de onCreate ela é chamada, então faço uma chamada POST na API do Telegram com o nome do canal, token do bot e conteúdo, que, no caso, é o texto da notícia.

Pregunta

E como os itens são salvos no Firebase?

Resposta: Recentemente, o Firebase lançou uma API para acessar a SDK usando Python, então eu escrevi um item pipeline chamado scrapy-firebase que usa essa API para escrever no banco de dados do Firebase, a cada item coletado do Scrapy, o método process_item do pipeline é chamado, e nesse método é salvo o item no Firebase.

class FirebasePipeline(BaseItemExporter):

    def load_spider(self, spider):
        self.crawler = spider.crawler
        self.settings = spider.settings

    def open_spider(self, spider):
        self.load_spider(spider)

        configuration = {
            'credential': credentials.Certificate(filename),
            'options': {'databaseURL': self.settings['FIREBASE_DATABASE']}
        }

        firebase_admin.initialize_app(**configuration)
        self.ref = db.reference(self.settings['FIREBASE_REF'])

    def process_item(self, item, spider):
        item = dict(self._get_serialized_fields(item))

        child = self.ref.child('/'.join([item[key] for key in self.keys]))
        child.set(item)
        return item

Próximos passos

Ao mesmo tempo em que eu notifico o canal do Telegram, estou usando o Cloud Natural Language API para classificar a notícia, e, em seguida, salvo no BigQuery. Após algum tempo, acredito que será possível usar o BigQuery para determinar quais trechos, quando e o quê costuma dar mais problemas à rodovia, através de data mining!

Código-fonte: https://github.com/skhaz/highway-overseer

Problema

por skhaz em 10 de July de 2017 às 00:00

June 27, 2017

Bruno Cezar Rocha

The quality of the python ecosystem

[en]

Recently I talked about The Quality of The Python Ecosystem in "Caipyra" a very nice conference in Ribeirão Preto, Brazil.

Here are the slides (in English and also in Portuguese) and some pictures of the awesome event!

[pt]

Recentemente falei sobre a Qualidade do Ecossitema Python no evento "Caipyra"  em Ribeirão Preto, Brasil.

Aqui estão os slides (em inglês e Português) e também algumas fotos desse evento maravilhoso!


Slides

[en]


[pt]


Pictures

See full picture collection here: https://www.flickr.com/photos/rdegiovani/sets/72157685348056266/


Extra notes and updates:

Nick Coghlan pointed to those 2 links:

OPenShift.io has built-in safety and analytics tools 

https://developers.redhat.com/blog/2017/05/02/increasing-developer-confidence-and-reducing-development-risk-with-red-hat-openshift-io-analytics/


The recommendation engine is open source

https://github.com/fabric8-analytics

por Bruno Rocha em 27 de June de 2017 às 22:57

June 17, 2017

Flávio Coelho

Curso de introdução a criptomoedas - Aula 01

For the Portuguese speaking readers of this blog, I am starting an Introductory course on Cryptocurrencies and applications on the blockchain which is an online version of a standard classroom course I am starting now at FGV. This is the first lecture which is basically an intro to the topic and the structure of the course. The online version should have the main content of the lectures on

por Flavio Coelho (noreply@blogger.com) em 17 de June de 2017 às 16:50

May 24, 2017

Filipe Saraiva

LaKademy 2017

Foto em grupo do LaKademy 2017

E chegamos à 5ª edição do encontro latino-americano do KDE, o LaKademy. Nesse tempo todo foi perceptível o crescimento da comunidade na região, em especial no Brasil, ainda que mantendo o fluxo típico dos trabalhos voluntários onde participantes vem e vão de acordo com suas demandas.

Dessa vez o evento saiu das praias cariocas e adentrou um pouco mais para o interior do país, subindo o morro urbano de Belo Horizonte. Cidade aprazível conhecida pelas cachaças, queijos, cervejas artesanais, queijos, ladeiras e queijos, Belo Horizonte combina um ar cosmopolita, com diversas opções de lazer, culinária e mais, com um jeito cordial e solícito típico de seus moradores. Adorei a cidade e espero um dia agendar uma viagem que não seja a trabalho para lá.

As atividades do LaKademy ocorreram nas dependências do CEFET, do final de abril ao início de maio, em pleno feriadão do dia do trabalhador combinado a uma greve geral dias antes. Muitos que participaram do evento (eu incluso) defendiam as pautas da greve, mas não podíamos abandonar o evento após todo o investimento feito pelo KDE. Portanto, fica aqui meu mea culpa sobre esse tema. 🙂

A exemplo das demais edições do evento trabalhei bastante no Cantor, software matemático o qual sou mantenedor. Dessa vez as principais tarefas que desenvolvi podem ser resumidas em um grande esforço de triagem: revisões de patches pendentes, uma extensa revisão para fechar todos os bugs antigos e inválidos existentes, deixando abertos apenas aqueles que importam, e outra revisão nas tarefas pendentes, em especial naquelas que estavam abertas há quase um ano mas cujo os desenvolvedores responsáveis não haviam realizado qualquer movimentação durante o referido tempo.

No campo das funcionalidades, finalizei uma refatoração nos backends para apresentar a versão recomendada da linguagem de programação no Cantor. Como cada linguagem tem seu próprio planejamento, é comum que de uma versão para outra o backend do Cantor comece a se comportar de maneira inesperada ou mesmo deixe de funcionar (Sage, estou pensando em você). Essa funcionalidade apresenta a versão “recomendada” da linguagem para o backend do Cantor, significando que essa versão descrita foi testada e sabemos que funcionará bem com a ferramenta. Isso serve como um workaround para manter a sanidade do desenvolvedor enquanto suporta 11 backends diferentes.

Outra funcionalidade que trabalhei mas ainda não finalizei foi a adição de um seletor de backends LaTeX para usar no Cantor. Atualmente existem muitas opções de processadores LaTeX (pdflatex, pdftex, luatex, xetex, …), alguns deles com muitas opções adicionais. Isso aumentaria a versatilidade do Cantor e permitira que processadores modernos possam ser utilizados no software.

Além dessas funcionalidades houveram correções de bugs e auxílio ao Fernando Telles em algumas tarefas sobre esse software.

Outras tarefas que desenvolvi nessa edição, também a exemplo das demais, foram as relacionadas com o gerenciamento e promoção do KDE Brasil. Nelas, pesquisei como trazer de volta o feed do Planet KDE Português (que o Fred acabou desenvolvendo), atualização dos feeds automáticos nas nossas redes sociais, atualização da conta de e-mail que utilizamos para gerenciar nossas redes, port do site do LaKademy para bootstrap (que acho q o pessoal não vai utilizar pois estão migrando para WordPress) e uma pesada triagem das tarefas no workboard do KDE Brasil. Além de tudo isso, ainda tivemos a famosa reunião de promo onde discutimos ações de promoção para o país e região – tudo também documentado no workboard.

E claro, assim como trabalhamos muito e de forma muito intensa esses dias todos, o LaKademy também é um momento de reencontrar amigos e afetos e se divertir bastante entre um push e outro. É sempre reconfortante encontrar a galera inteira, e fica o convite para que os calouros apareçam sempre.

Uma falta da edição desse ano foi a ausência de não brasileiros – precisamos pensar em estratégias de termos latino-americanos de outros países participando do LaKademy. Seria ruim que o evento passasse a ser tão somente um Akademy-BR.

Filipe e Chicão

Para finalizar, deixo meu agradecimento à comunidade e minha disposição para continuar trabalhando para tornar a América Latina uma região cada vez mais importante para o desenvolvimento e futuro do KDE.

por Filipe Saraiva em 24 de May de 2017 às 13:38

May 11, 2017

Vinta Software

We have new positions available for Full Stack Developers!

Here we go again!

Due to our continued growth, we are hiring developers for a Full Stack position! We are looking for junior to senior developers experienced in Django and Javascript. React is a plus. Alt text

Interested?

Send your GITHUB and resume to jobs+2017.2@vinta.com.br with your name on the subject.

por Felipe Farias em 11 de May de 2017 às 16:23

May 05, 2017

Magnun Leno

April 26, 2017

PythonClub

What the Flask? pt 4 - Extensões para o Flask

What The Flask - 4/5

Finalmente!!! Depois de uma longa espera o What The Flask está de volta! A idéia era publicar primeiro a parte 4 (sobre Blueprints) e só depois a 5 sobre como criar extensões. Mas esses 2 temas estão muito interligados então neste artigo os 2 assuntos serão abordados. E a parte 5 será a final falando sobre deploy!

code
  1. Hello Flask: Introdução ao desenvolvimento web com Flask
  2. Flask patterns: Estruturando aplicações Flask
  3. Plug & Use: extensões essenciais para iniciar seu projeto
  4. Magic(app): Criando Extensões para o Flask(<-- Você está aqui)
  5. Run Flask Run: "deploiando" seu app nos principais web servers e na nuvem

Não sei se você ainda se lembra? mas estavámos desenvolvendo um CMS de notícias, utilizamos as extensões Flask-MongoEngine, Flask-Security, Flask-Admin e Flask-Bootstrap.

E neste artigo iremos adicionar mais uma extensão em nosso CMS, mas iremos criar uma extensão ao invés de usar uma das extensões disponíveis.

Extensão ou Plugin? Por definição plugins diferem de extensões. Plugins geralmente são externos e utilizam algum tipo de API pública para se integrar com o aplicativo. Extensões, por outro lado, geralmente são integradas com a lógica da aplicação, isto é, as interfaces do próprio framework. Ambos, plugins e extensões, aumentam a utilidade da aplicação original, mas plugin é algo relacionado apenas a camadas de mais alto nível da aplicação, enquanto extensões estão acopladas ao framework. Em outras palavras, plugin é algo que você escreve pensando apenas na sua aplicação e está altamente acoplado a ela enquanto extensão é algo que pode ser usado por qualquer aplicação escrita no mesmo framework pois está acoplado a lógica do framework e não das aplicações escritas com ele.

Quando criar uma extensão?

Faz sentido criar uma extensão quando você identifica uma functionalidade que pode ser reaproveitada por outras aplicações Flask, assim você mesmo se beneficia do fato de não precisar reescrever (copy-paste) aquela funcionalidade em outros apps e também pode publicar sua extensão como open-source beneficiando toda a comunidade e incorporando as melhorias, ou seja, todo mundo ganha!

Exemplo prático

Imagine que você está publicando seu site mas gostaria de prover um sitemap. (URL que lista todas as páginas existentes no seu site usada pelo Google para melhorar a sua classificação nas buscas).

Como veremos no exemplo abaixo publicar um sitemap é uma tarefa bastante simples, mas é uma coisa que você precisará fazer em todos os sites que desenvolver e que pode se tornar uma funcionalidade mais complexa na medida que necessitar controlar datas de publicação e extração de URLs automáticamente.

Exemplo 1 - Publicando o sitemap sem o uso de extensões

from flask import Flask, make_response

app = Flask(__name__)

@app.route('/artigos')
def artigos():
    "este endpoint retorna a lista de artigos"

@app.route('/paginas')
def paginas():
    "este endpoint retorna a lista de paginas"

@app.route('/contato')
def contato():
    "este endpoint retorna o form de contato"

######################################
# Esta parte poderia ser uma extensão
######################################
@app.route('/sitemap.xml')
def sitemap():
    items = [
        '<url><loc>{0}</loc></url>'.format(page)
        for page in ['/artigos', '/paginas', '/contato']
    ]
    sitemap_xml = (
        '<?xml version="1.0" encoding="UTF-8"?>'
        '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">{0}</urlset>'
    ).format(''.join(items)).strip()
    response = make_response(sitemap_xml)
    response.headers['Content-Type'] = 'application/xml'
    return response
#######################################
# / Esta parte poderia ser uma extensão
#######################################


app.run(debug=True)

Executando e acessando http://localhost:5000/sitemap.xml o resultado será:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url><loc>/artigos</loc></url>
    <url><loc>/paginas</loc></url>
    <url><loc>/contato</loc></url>
</urlset>

NOTE: O app acima é apenas um simples exemplo de como gerar um sitemap e a intenção dele aqui é apenas a de servir de exemplo para extensão que criaremos nos próximos passos, existem outras boas práticas a serem seguidas na publicação de sitemap mas não é o foco deste tutorial.

Vamos então transformar o exemplo acima em uma Extensão e utilizar uma abordagem mais dinâmica para coletar as URLs, mas antes vamos entender como funcionam as extensões no Flask.

Como funciona uma Extensão do Flask?

Lembra que na parte 2 desta série falamos sobre os patterns do Flask e sobre o application factory e blueprints? As extensões irão seguir estes mesmos padrões em sua arquitetura.

O grande "segredo" para se trabalhar com Flask é entender que sempre iremos interagir com uma instância geralmente chamada de app e que pode ser acessada também através do proxy current_app e que sempre aplicaremos um padrão que é quase funcional neste deste objeto sendo que a grande diferença aqui é que neste paradigma do Flask as funções (chamadas de factories) introduzem side effects, ou seja, elas alteram ou injetam funcionalidades no app que é manipulado até que chega ao seu estado de execução. (enquanto em um paradigma funcional as funções não podem ter side effects)

Também é importante entender os estados configuração, request e interativo/execução do Flask, asunto que abordamos na parte 1 desta série.

Em resumo, iremos criar uma factory que recebe uma instância da classe Flask, o objeto app (ou o acessa através do proxy current_app) e então altera ou injeta funcionalidades neste objeto.

Dominar o Flask depende muito do entendimento desse padrão de factories e os 3 estados da app citados acima, se você não está seguro quanto a estes conceitos aconselho reler as partes 1 e 2 desta série (e é claro sinta se livre para deixar comentários com as suas dúvidas).

Só para relembrar veja o seguinte exemplo:

app = Flask(__name__)  # criamos a instancia de app
admin = Admin()  # instancia do Flask-Admin ainda não inicializada

do_something(app)  # injeta ou altera funcionalidades do app
do_another_thing(app, admin)  # injeta ou altera funcionalidades do apo ou do admin
Security(app)  # adiciona funcionalidades de login e controle de acesso
Cache(app)  # adiciona cache para views e templates
SimpleSitemap(app)  # A extensão que iremos criar! Ela adiciona o /sitemap.xml no app

admin.init_app(app)  # agora sim inicializamos o flask-admin no modo lazy

Olhando o código acima pode parecer bastante simples, você pode achar que basta receber a intância de app e sair alterando sem seguir nenhum padrão.

def bad_example_of_flask_extension(app):
    "Mal exemplo de factory que injeta ou altera funcionalidades do app"
    # adiciona rotas
    @app.route('/qualquercoisa)
    def qualquercoisa():
        ...
    # substitui objetos usando composição
    app.config_class = MyCustomConfigclass
    # altera config
    app.config['QUALQUERCOISA'] = 'QUALQUERVALOR'
    # sobrescreve métodos e atributos do app
    app.make_responde = another_function
    # Qualquer coisa que o Python (e a sua consciência) permita!

Isso pode provavelmente funcionar mas não é uma boa prática, existem muitos problemas com o factory acima e alguns deles são:

  1. Nunca devemos definir rotas dessa maneira com app.route em uma extensão o correto é usar blueprints.
  2. Lembre-se dos 3 estados do Flask, devemos levar em consideração que no momento que a aplicação for iniciada a extensão pode ainda não estar pronta para ser carregada, por exemplo, a sua extensão pode depender de um banco de dados que ainda não foi inicializado, portanto as extensões precisam sempre ter um modo lazy.
  3. Usar funções pode se ruma boa idéia na maioria dos casos, mas lembre-se que precisamos manter estado em algumas situações então pode ser melhor usar classes ao invés de funções pois as classes permitem uma construção mais dinâmica.
  4. Nossa extensão precisa ser reutilizavel em qualquer app flask, portanto devemos usar namespaces ao ler configurações e definir rotas.

NOTE: Eu NÃO estou dizendo que você não deve usar funções para extender seu app Flask, eu mesmo faço isso em muitos casos. Apenas tenha em mente esses detalhes citados na hora de decidir qual abordagem usar.

Patterns of a Flask extension

Preferencialmente uma Extensão do Flask deve seguir esses padrões:

  • Estar em um módulo nomeado com o prefixo flask_ como por exemplo flask_admin e flask_login e neste artigo criaremos o flask_simple_sitemap. (NOTE: Antigamente as extensões usavam o padrão flask.ext.nome_extensao mas este tipo de plublicação de módulo com namespace do flask.ext foi descontinuado e não é mais recomendado.)
  • Fornecer um método de inicialização lazy nomeado init_app.
  • Ler todas as suas configurações a partir do app.config
  • Ter suas configurações prefixadas com o nome da extensão, exemplo: SIMPLE_SITEMAP_URLS ao invés de apenas SITEMAP_URLS pois isto evita conflitos com configurações de outras extensões.
  • Caso a extensão adicione views e URL rules, isto deve ser feito com Blueprint
  • Caso a extensão adicione arquivos estáticos ou de template isto também deve ser feito com Blueprint
  • ao registrar urls e endpoints permitir que sejam dinâmicos através de config e sempre prefixar com o nome da extensão. Exemplo: url_for('simple_sitemap.sitemap') é melhor do que url_for('sitemap') para evitar conflitos com outras extensões.

NOTE: Tenha em mente que regras foram feitas para serem quebradas, O Guido escreveu na PEP8 "A Foolish Consistency is the Hobgoblin of Little Minds", ou seja, tenha os padrões como guia mas nunca deixe que eles atrapalhem o seu objetivo. Eu mesmo já quebrei essa regra 1 no flasgger, eu poderia ter chamado de flask_openapi ou flask_swaggerui mas achei Flasgger um nome mais divertido :)

De todos os padrões acima o mais importante é o de evitar o conflito com outras extensões!

Zen do Python: Namespaces são uma ótima idéia! vamos usar mais deles!

Criando a extensão Simple Sitemap

Ok, agora que você já sabe a teoria vamos colocar em prática, abre ai o vim, emacs, pycharm ou seu vscode e vamos reescrever o nosso app do Exemplo 1 usando uma extensão chamada flask_simple_sitemap e para isso vamos começar criando a extensão:

A extensão será um novo módulo Python que vai ser instalado usando setup ou pip portanto crie um projeto separado.

Em seu terminal *nix execute os comandos:

➤ $
# Entre na pasta onde vc armazena seus projetos
cd Projects

# crie o diretório root do projeto
mkdir simple_sitemap
cd simple_sitemap

# torna a extensão instalável (vamos escrever esse arquivo depois)
touch setup.py

# é sempre bom incluir uma documentação básica
echo '# Prometo documentar essa extensão!' > README.md

# se não tiver testes não serve para nada! :)
touch tests.py

# crie o diretório que será o nosso package
mkdir flask_simple_sitemap

# __init__.py para transformar o diretório em Python package
echo 'from .base import SimpleSitemap' > flask_simple_sitemap/__init__.py

# A implementação da extensão será escrita neste arquivo
# (evite usar main.py pois este nome é reservado para .zipped packages)
touch flask_simple_sitemap/base.py

# Crie a pasta de templates
mkdir flask_simple_sitemap/templates

# usaremos Jinja para gerar o XML
touch flask_simple_sitemap/templates/sitemap.xml

# incluindo templates no build manifest do setuptools
echo 'recursive-include flask_simple_sitemap/templates *' > MANIFEST.in

# precisaremos de um arquivo de requirements para os testes
touch requirements-test.txt

Agora voce terá a seguinte estrutura:

➤ tree
simple_sitemap/
├── flask_simple_sitemap/
│   ├── base.py
│   ├── __init__.py
│   └── templates/
│       └── sitemap.xml
├── MANIFEST.in
├── README.md
├── requirements-test.txt
├── setup.py
└── tests.py

2 directories, 8 files

O primeiro passo é escrever o setup.py já que a extensão precisa ser instalavél:

from setuptools import setup, find_packages

setup(
    name='flask_simple_sitemap',
    version='0.0.1',
    packages=find_packages(),
    include_package_data=True,
    zip_safe=False
)

Eu tenho o costumo de praticar o que eu chamo de RDD (Readme Driven Ddevelopment), ou seja, ao criar projetos como este eu costumo escreve primeiro o README.md explicando como deve funcionar e só depois de ter isto pronto que eu começo a programar.

Edite o README.md

# Flask Simple Sitemap

Esta extensão adiciona a funcionalidade de geração de sitemap ao seu app flask.

## Como instalar?

Para instalar basta clonar o repositório e executar:

    $ python setup.py install

Ou via `pip install flask_simple_sitemap`

## Como usar?

Basta importar e inicializar:

    from flask import Flask
    from flask_simple_sitemap import SimpleSitemap

    app = Flask(__name__)
    SimpleSitemap(app)

    @app.route('/)
    def index():
        return 'Hello World'

Como em toda extensão Flask também é possível inicializar no modo Lazy chamando
o método `init_app`

## Opções de configuração:

esta extensão utiliza o namespace de configuração `SIMPLE_SITEMAP_`

- **SIMPLE_SITEMAP_BLUEPRINT** define o nome do blueprint e do url prefix (default: `'simple_sitemap'`)
- **SIMPLE_SITEMAP_URL** define a url que irá renderizar o sitemap (default: `'/sitemap.xml'`)
- **SIMPLE_SITEMAP_PATHS** dicionário de URLs a serem adicionadas ao sitemap (exemplo: URLs criadas a partir de posts em bancos de dados)

Agora que já sabemos pelo README o que queremos entregar de funcionalidade já é possível escrever o tests.py e aplicar também um pouco de TDD

O Flask tem uma integração bem interesante com o py.test e podemos editar o tests.py da seguinte maneira:

NOTE: O ideal é fazer o test setup no arquivo conftest.py e usar fixtures do py.test, mas aqui vamos escrever tudo junto no tests.py para ficar mais prático.

Zen do Python: praticidade vence a pureza :)

####################################################################
# Início do Test Setup
#

import xmltodict
from flask import Flask
from flask_simple_sitemap import SimpleSitemap

app = Flask(__name__)
extension = SimpleSitemap()

app.config['SIMPLE_SITEMAP_BLUEPRINT'] = 'test_sitemap'
app.config['SIMPLE_SITEMAP_URL'] = '/test_sitemap.xml'
app.config['SIMPLE_SITEMAP_PATHS'] = {
    '/this_is_a_test': {'lastmod': '2017-04-24'}
}

@app.route('/hello')
def hello():
    return 'Hello'

# assert lazy initialization
extension.init_app(app)

client = app.test_client()

#
# Final Test Setup
####################################################################

####################################################################
# Cláusula que Permite testar manualmente o app com `python tests.py`
#
if __name__ == '__main__':
    app.run(debug=True)
#
# acesse localhost:5000/test_sitemap.xml
####################################################################

####################################################################
# Agora sim os testes que serão executados com `py.test tests.py -v`
#

def test_sitemap_uses_custom_url():
    response = client.get('/test_sitemap.xml')
    assert response.status_code == 200

def test_generated_sitemap_xml_is_valid():
    response = client.get('/test_sitemap.xml')
    xml = response.data.decode('utf-8')
    result = xmltodict.parse(xml)
    assert 'urlset' in result
    # rules are Ordered
    assert result['urlset']['url'][0]['loc'] == '/test_sitemap.xml'
    assert result['urlset']['url'][1]['loc'] == '/hello'
    assert result['urlset']['url'][2]['loc'] == '/this_is_a_test'

#
# Ao terminar o tutorial reescreva esses testes usando fixtures :)
# E é claro poderá adicionar mais testes!
###################################################################

Para que os testes acima sejam executados precisamos instalar algumas dependencias portanto o requirements-test.txt precisa deste conteúdo:

flask
pytest
xmltodict
--editable .

NOTE: ao usar --editable . no arquivo de requirements você faz com que a extensão seja auto instalada em modo de edição desta forma executamos apenas pip install -r requirements-test.txt e o pip se encarrega de rodar o python setup.py develop.

Vamos então começar a desenvolver editando o front-end da extensão que será escrito no template: flask_simple_sitemap/templates/sitemap.xml este template espera um dict paths chaveado pela location e contento sitemap tag names em seu valor. exemplo paths = {'/artigos': {'lastmod': '2017-04-24'}, ...}

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    {% for loc, data in paths.items() %}
    <url>
        <loc>{{loc|safe}}</loc>
        {% for tag_name, value in data.items() %}
            <{{tag_name}}>{{value}}</{{tag_name}}>
        {% endfor %}
    </url>
    {% endfor %}
</urlset>

E então finalmente escreveremos a classe base da extensão no arquivo flask_simple_sitemap/base.py

Lembrando de algumas boas práticas:

  • Prover um método de inicialização lazy com a assinatura init_app(self, app)
  • Impedir registro em duplicidade e inserir um registro no app.extensions
  • Ao adicionar rotas sempre usar Blueprints e namespaces (o Blueprint já se encarrega do namespace nas urls)
  • Configs devem sempre ter um prefixo, faremos isso com o get_namespace que aprendemos na parte 1

NOTE: Leia atentamente os comentários e docstrings do código abaixo.

# coding: utf-8
from flask import Blueprint, render_template, make_response


class SimpleSitemap(object):
    "Extensão Flask para publicação de sitemap"

    def __init__(self, app=None):
        """Define valores padrão para a extensão
        e caso o `app` seja informado efetua a inicialização imeditatamente
        caso o `app` não seja passado então
        a inicialização deverá ser feita depois (`lazy`)
        """
        self.config = {
            'blueprint': 'simple_sitemap',
            'url': '/sitemap.xml',
            'paths': {}
        }
        self.app = None  # indica uma extensão não inicializada

        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        """Método que Inicializa a extensão e
        pode ser chamado de forma `lazy`.

        É interessante que este método seja apenas o `entry point` da extensão
        e que todas as operações de inicialização sejam feitas em métodos
        auxiliares privados para melhor organização e manutenção do código.
        """
        self._register(app)
        self._load_config()
        self._register_view()

    def _register(self, app):
        """De acordo com as boas práticas para extensões devemos checar se
        a extensão já foi inicializada e então falhar explicitamente caso
        seja verdadeiro.
        Se tudo estiver ok, então registramos o app.extensions e o self.app
        """
        if not hasattr(app, 'extensions'):
            app.extensions = {}

        if 'simple_sitemap' in app.extensions:
            raise RuntimeError("Flask extension already initialized")

        # se tudo está ok! então registramos a extensão no app.extensions!
        app.extensions['simple_sitemap'] = self

        # Marcamos esta extensão como inicializada
        self.app = app

    def _load_config(self):
        """Carrega todas as variaveis de config que tenham o prefixo `SIMPLE_SITEMAP_`
        Por exemplo, se no config estiver especificado:

            SIMPLE_SITEMAP_URL = '/sitemap.xml'

        Podemos acessar dentro da extensão da seguinte maneira:

           self.config['url']

        e isto é possível por causa do `get_namespace` do Flask utilizado abaixo.
        """
        self.config.update(
            self.app.config.get_namespace(
                namespace='SIMPLE_SITEMAP_',
                lowercase=True,
                trim_namespace=True
            )
        )

    def _register_view(self):
        """aqui registramos o blueprint contendo a rota `/sitemap.xml`"""
        self.blueprint = Blueprint(
            # O nome do blueprint deve ser unico
            # usaremos o valor informado em `SIMPLE_SITEMAP_BLUEPRINT`
            self.config['blueprint'],

            # Agora passamos o nome do módulo Python que o Blueprint
            # está localizado, o Flask usa isso para carregar os templates
            __name__,

            # informamos que a pasta de templates será a `templates`
            # já é a pasta default do Flask mas como a nossa extensão está
            # adicionando um arquivo na árvore de templates será necessário
            # informar
            template_folder='templates'
        )

        # inserimos a rota atráves do método `add_url_rule` pois fica
        # esteticamente mais bonito do que usar @self.blueprint.route()
        self.blueprint.add_url_rule(
            self.config['url'],  # /sitemap.xml é o default
            endpoint='sitemap',
            view_func=self.sitemap_view,  # usamos outro método como view
            methods=['GET']
        )

        # agora só falta registar o blueprint na app
        self.app.register_blueprint(self.blueprint)

    @property
    def paths(self):
        """Cria a lista de URLs que será adicionada ao sitemap.

        Esta property será executada apenas quando a URL `/sitemap.xml` for requisitada

        É interessante ter este método seja público pois permite que seja sobrescrito
        e é neste método que vamos misturar as URLs especificadas no config com
        as urls extraidas do roteamento do Flask (Werkzeug URL Rules).

        Para carregar URLs dinâmicamente (de bancos de dados) o usuário da extensão
        poderá sobrescrever este método ou contribur com o `SIMPLE_SITEMAP_PATHS`

        Como não queremos que exista duplicação de URLs usamos um dict onde
        a chave é a url e o valor é um dicionário completando os dados ex:

        app.config['SIMPLE_SITEMAP_PATHS'] = {
            '/artigos': {
                'lastmod': '2017-01-01'
            },
            ...
        }
        """

        paths = {}

        # 1) Primeiro extraimos todas as URLs registradas na app
        for rule in self.app.url_map.iter_rules():
            # Adicionamos apenas GET que não receba argumentos
            if 'GET' in rule.methods and len(rule.arguments) == 0:
                # para urls que não contém `lastmod` inicializamos com
                # um dicionário vazio
                paths[rule.rule] = {}

        # caso existam URLs que recebam argumentos então deverão ser carregadas
        # de forma dinâmica pelo usuário da extensão
        # faremos isso na hora de usar essa extensão no CMS de notícias.

        # 2) Agora carregamos URLs informadas na config
        # isso é fácil pois já temos o config carregado no _load_config
        paths.update(self.config['paths'])

        # 3) Precisamos sempre retornar o `paths` neste método pois isso permite
        # que ele seja sobrescrito com o uso de super(....)
        return paths

    def sitemap_view(self):
        "Esta é a view exposta pela url `/sitemap.xml`"
        # geramos o XML através da renderização do template `sitemap.xml`
        sitemap_xml = render_template('sitemap.xml', paths=self.paths)
        response = make_response(sitemap_xml)
        response.headers['Content-Type'] = 'application/xml'
        return response

NOTE: Neste exemplo usamos um método de desenvolvimento muito legal que eu chamo de:
ITF (Important Things First) onde Arquitetura, Documentação, Testes e Front End (e protótipos) são muito mais importantes do que a implementação de back end em si.
Assumimos que caso a nossa implementação seja alterada os conceitos anteriores se mantém integros com a proposta do produto.
Ordem de prioridade no projeto: 1) Definimos a arquitetura 2) Escrevemos documentação 3) Escrevemos testes 4) Implementamos front end (e protótipo) 5) back end é o menos importante do ponto de vista do produto e por isso ficou para o final! :)

O código da extensão etá disponível em http://github.com/rochacbruno/flask_simple_sitemap

Usando a extensão em nosso CMS de notícias

Agora vem a melhor parte, usar a extensão recém criada em nosso projeto existente.

O repositório do CMS está no github Precisamos do MongoDB em execução e a forma mais fácil é através do docker

➤ docker run -d -p 27017:27017 mongo

Se preferir utilize uma instância do MongoDB instalada localmente ou um Mongo As a Service.

NOTE: O modo de execução acima é efemero e não persiste os dados, para persistir use -v $PWD/etc/mongodata:/data/db.

Agora que o Mongo está rodando execute o nosso CMS.

Obtendo, instalando e executando:

➤
git clone -b extended --single-branch https://github.com/rochacbruno/wtf.git extended
cd wtf

# adicione nossa extensao nos requirements do CMS 
# sim eu publiquei no PyPI, mas se preferir instale a partir do fonte que vc escreveu
echo 'flask_simple_sitemap' >> requirements.txt

# activate a virtualenv
pip install -r requirements.txt

# execute
python run.py 

Agora com o CMS executando acesse http://localhost:5000 e verá a seguinte tela:

cms

Os detalhes dessa aplicação você deve ser lembrar pois estão nas partes 1, 2 e 3 deste tutorial.

Agora você pode se registrar novas notícias usando o link cadastro e precisará efetuar login e para isso deve se registrar como usuário do aplicativo.

Temos as seguintes urls publicads no CMS

  • '/' lista todas as noticias na home page
  • '/noticias/cadastro' exibe um formulário para incluir noticias
  • '/noticia/<id> acessa uma noticia especifica
  • '/admin' instancia do Flask Admin para adminstrar usuários e o banco de dados

Agora vamos incluir a extensão flask_simple_sitemap que criamos e adicionar as URLs das noticias dinâmicamente.

Edite o arquico wtf/news_app.py incluindo a extensão flask_simple_sitemap e também adicionando as URLs de todas as noticias que existirem no banco de dados.

# coding: utf-8
from os import path
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_security import Security, MongoEngineUserDatastore
from flask_debugtoolbar import DebugToolbarExtension

###############################################
# 1) importe a nossa nova extensão
from flask_simple_sitemap import SimpleSitemap

from .admin import configure_admin
from .blueprints.noticias import noticias_blueprint
from .db import db
from .security_models import User, Role
from .cache import cache

##############################################
# 2) importe o model de Noticia
from .models import Noticia


def create_app(mode):
    instance_path = path.join(
        path.abspath(path.dirname(__file__)), "%s_instance" % mode
    )

    app = Flask("wtf",
                instance_path=instance_path,
                instance_relative_config=True)

    app.config.from_object('wtf.default_settings')
    app.config.from_pyfile('config.cfg')

    app.config['MEDIA_ROOT'] = path.join(
        app.config.get('PROJECT_ROOT'),
        app.instance_path,
        app.config.get('MEDIA_FOLDER')
    )

    app.register_blueprint(noticias_blueprint)

    Bootstrap(app)
    db.init_app(app)
    Security(app=app, datastore=MongoEngineUserDatastore(db, User, Role))
    configure_admin(app)
    DebugToolbarExtension(app)
    cache.init_app(app)

    ############################################
    # 3) Adicionane as noticias ao sitemap
    app.config['SIMPLE_SITEMAP_PATHS'] = {
        '/noticia/{0}'.format(noticia.id): {} # dict vazio mesmo por enquanto!
        for noticia in Noticia.objects.all()
    }

    ############################################
    # 4) Inicialize a extensão SimpleSitemap
    sitemap = SimpleSitemap(app)

    return app

Agora execute o python run.py e acesse http://localhost:5000/sitemap.xml

Você verá o sitemap gerado incluindo as URLs das notícias cadastradas!

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
  <loc>/noticia/58ffe998e138231eef84f9a7</loc>
</url>
<url>
  <loc>/noticias/cadastro</loc>
</url>
<url>
  <loc>/</loc>
</url>
...
# + um monte de URL do /admin aqui
</urlset>

NOTE: Funcionou! legal! porém ainda não está bom. Existem algumas melhorias a serem feitas e vou deixar essas melhorias para você fazer!

Desafio What The Flask!

1) Melhore a geração de URLs do CMS

Você reparou que a URL das notícias está bem feia? /noticia/58ffe998e138231eef84f9a7 não é uma boa URL Para ficar mais simples no começo optamos por usar o id da notícia como URL mas isso não é uma boa prática e o pior é que isso introduz até mesmo problemas de segurança.

Você conseguer arrumar isso? transformando em: /noticia/titulo-da-noticia-aqui ?

Vou dar umas dicas:

Altere o Model:

  • Altere o model Noticia em: https://github.com/rochacbruno/wtf/blob/extended/wtf/models.py#L5.
  • Insira um novo campo para armazenar o slug da notícia, o valor será o título transformado para lowercase, espaços substituidos por -. Ex: para o título: 'Isto é uma notícia' será salvo o slug: 'isto-e-uma-noticia'.
  • Utilize o método save do MongoEngine ou se preferir use signals para gerar o slug da notícia.
  • Utilize o módulo awesome-slugify disponível no PyPI para criar o slug a partir do título.

Altere a view:

Altere as urls passadas ao SIMPLE_SITEMAP_PATHS usando o slug ao invés do id.

2) Adicione data de publicação nas notícias

Reparou que o sitemap está sem a data da notícia? adicione o campo modified ao model Noticia e faça com que ele salve a data de criação e/ou alteração da notícia.

Queremos algo como:

    app.config['SIMPLE_SITEMAP_PATHS'] = {
        '/noticia/{0}'.format(noticia.slug): {
            'lastmod': noticia.modified.strftime('%Y-%m-%d')
        }
        for noticia in Noticia.objects.all()
    }

Para gerar no sitemap:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
  <loc>/noticia/titulo-da-noticia</loc>
  <lastmod>2017-04-25</lastmod>
</url>
...

3) Crie uma opção de filtros na extensão simple_sitemap

Uma coisa chata também é o fato do sitemap.xml ter sido gerado com esse monte de URL indesejada. As URLs iniciadas com /admin por exemplo não precisam ir para o sitemap.xml.

Implemente esta funcionalidade a extensão:

DICA: Use regex import re

app.config['SIMPLE_SITEMAP_EXCLUDE'] = [
    # urls que derem match com estes filtros não serão adicionadas ao sitemap
    '^/admin/.*'
]

DESAFIO: Após implementar as melhorias inclua nos comentários um link para a sua solução, pode ser um fork dos repositórios ou até mesmo um link para gist ou pastebin (enviarei uns adesivos de Flask para quem completar o desafio!)


O diff com as alterações realizadas no CMS encontra-se no github.com/rochacbruno/wtf
A versão final da extensão SimpleSitemap está no github.com/rochacbruno/flask_simple_sitemap
A versão final do CMS app está no github.com/rochacbruno/wtf

Se você está a procura de uma extensão para sitemap para uso em produção aconselho a flask_sitemap


END: Sim chegamos ao fim desta quarta parte da série What The Flask. Eu espero que você tenha aproveitado as dicas aqui mencionadas. Nas próximas partes iremos efetuar o deploy de aplicativos Flask. Acompanhe o PythonClub, o meu site e meu twitter para ficar sabendo quando a próxima parte for publicada.


PUBLICIDADE: Iniciarei um curso online de Python e Flask, para iniciantes abordando com muito mais detalhes e exemplos práticos os temas desta série de artigos e muitas outras coisas envolvendo Python e Flask, o curso será oferecido no CursoDePython.com.br, ainda não tenho detalhes especificos sobre o valor do curso, mas garanto que será um preço justo e acessível. Caso você tenha interesse por favor preencha este formulário pois dependendo da quantidade de pessoas interessadas o curso sairá mais rapidamente.


PUBLICIDADE 2: Também estou escrevendo um livro de receitas Flask CookBook através da plataforma LeanPub, caso tenha interesse por favor preenche o formulário na página do livro


PUBLICIDADE 3: Inscreva-se no meu novo canal de tutoriais

Muito obrigado e aguardo seu feedback com dúvidas, sugestões, correções etc na caixa de comentários abaixo.

Abraço! "Python é vida!"

por Bruno Cezar Rocha em 26 de April de 2017 às 12:00

April 23, 2017

PythonClub

Configurando OpenShift com Python 3.5 + Flask + Gunicorn

Configurando OpenShift com Python 3.5

Introdução

O OpenShift é uma plataforma de PasS que possibilita aos desenvolvedores "subir" aplicações na nuvem de uma maneira simples e rápida. Ele funciona a partir de gears(engrenagens) que representam máquinas que irão rodar as aplicações. Dentro de cada gear é possível instalar serviços, os são chamados de "cartridges".

Existem 3 planos:

  • Online (gratuito, com três gears)
  • Enterprise (pago com suporte)
  • Origin (versão da comunidade e pode ser utilizado livremente)

Um problema que me deparei ao utilizar o Openshift é que ele não possui um cartridge com Python3.5. Porém existe uma forma um pouco mais complicada de resolver esse problema.

Após fazer seu cadastro no OpenShift e instalar o client tools que contém as ferramentas necessárias para configurar nossa aplicação.

Após tudo isso vamos colocar a mão na massa, abra seu terminal e vamos lá.

Criando a aplicação

rhc app create <app-name> https://raw.githubusercontent.com/Grief/openshift-cartridge-python-3.5/master/metadata/manifest.yml diy-0.1

Substituindo "<app-name>" pelo nome de sua aplicação. O arquivo manifest.yml criado por Changaco(github) e "forkeado" por Grief(github) contém as configurações de um cartridge customizado que contém o python 3.5.

Para os curiosos o conteúdo do arquivo

---
Name: python
Cartridge-Short-Name: PYTHON
Display-Name: Only Python
Description: 'An embedded cartridge that provides only python, nothing else.'
Version: '3.5.0'
Versions: ['3.5.0', '2.7.11']
License: The Python License
License-Url: http://docs.python.org/3/license.html
Vendor: python.org
Cartridge-Version: 0.0.2
Cartridge-Vendor: praisebetoscience
Categories:
- service
- python
- embedded
Website: https://github.com/praisebetoscience/openshift-cartridge-python-3.5
Help-Topics:
  Developer Center: https://www.openshift.com/developers
Provides:
- python
Publishes:
Subscribes:
  set-env:
    Type: ENV:*
    Required: false
  set-doc-url:
    Type: STRING:urlpath
    Required: false
Scaling:
  Min: 1
  Max: -1
Version-Overrides:
  '2.7.11':
    Display-Name: Python 2.7
    License: The Python License, version 2.7
    Provides:
    - python-2.7
    - python
    - python(version) = 2.7
  '3.5.0':
    Display-Name: Python 3.5
    License: The Python License, version 3.5
    Provides:
    - python-3.5
    - python
    - python(version) = 3.5

Após isso sua aplicação já estárá executando, caso deseje acessar o endereço da mesma deverá ser http://<app-name>-.rhcloud.com. Você verá que a página do seu projeto não é nada mais do que o diy (Dot It Yourself), que é uma aplicação Ruby de exemplo que você pode alterar, e é o que vamos fazer.

Se você acessar o diretório do seu projeto verá que existe um diretório ".openshift", dentro desse diretório existe um outro diretório chamado "action_hooks", e dentro desse diretório existem dois arquivos "start" e "stop".

  • "<app-name>/.openshift/action_hooks/start"
  • "<app-name>/.openshift/action_hooks/stop"

Os dois arquivos são respectivamente os comandos para "subir" e "pausar" sua aplicação.

Flask

Vamos criar um projeto de exemplo, bem simples, que apenas nos retorne a versão do python utilizada. Primeiramente vamos criar nosso requirements.txt, com gunicorn e o flask.

"requirements.txt"

gunicorn
flask

Depois disso vamos criar o arquivo app.py que conterá nossa aplicação.

"app.py"

import sys
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return sys.version

Após isso basta fazer o commit de suas alterações.

shell git add . git commit -am 'Minhas alterações'

Após isso você verá que sua aplicação não está rodando, pois ainda não alteramos os arquivos "start" e "stop".

Configurando o Gunicorn no Start e Stop

O projeto diy do openshift nos deixa uma variável de ambiente $OPENSHIFT_DIY_IP com o IP da máquina, dessa forma podemos passar a variável e porta ao gunicorn.

"start"

#!/bin/bash
nohup $HOME/python/usr/bin/pip3 install -r $OPENSHIFT_REPO_DIR/requirements.txt
cd $OPENSHIFT_REPO_DIR
nohup $HOME/python/usr/bin/gunicorn app:app --bind=$OPENSHIFT_DIY_IP:8080 |& /usr/bin/logshifter -tag diy &

A primeira linha é o Shebang, o que significa que esse arquivo será executado pelo bash. Na segunda linha vemos nohup, que executa os comandos em uma sessão separada, vemos logo apóes vemos o uma chamada ao pip para instalar nossas dependências. Na terceira linha vemos o nohup, e depois o gunicorn inicializa nossa aplicação flask.

Isso só funciona pois o cartridge customizado instala o python3.5 dentro da pasta home do servidor do openshift.

"stop"

#!/bin/bash
source $OPENSHIFT_CARTRIDGE_SDK_BASH

# The logic to stop your application should be put in this script.
if [ -z "$(ps -ef | grep gunicorn | grep -v grep)" ]
then
    client_result "Application is already stopped"
else
    kill `ps -ef | grep gunicorn | grep -v grep | awk '{ print $2 }'` > /dev/null 2>&1
fi

Podemos ver que o comando ps procura algum processo do gunicorn, caso ele exista o kill será chamado.

Após isso, é só fazer o commit das alterações e você verá sua aplicação rodando.

Espero que o post ajude a quem quer subir alguma aplicação com python3.5 e só tem o heroku como opção.

Referências

por Horácio Dias em 23 de April de 2017 às 23:37

April 16, 2017

Magnun Leno

Hack ‘n’ Cast v1.6 - Espresso #002: Colisão SHA-1

Corram todos para as montanhas, o SHA-1 é oficialmente inseguro!

Baixe o episódio e leia o shownotes

por Magnun em 16 de April de 2017 às 19:12

March 29, 2017

Bruno Cezar Rocha

Consumindo e Publicando web APIs - PyData São Paulo - 2017

Consumindo e publicando web APIs apresentado no PyData SP no auditório da NuBank em 28 de Março de 2017

  1. O que são web APIs
  2. Consumindo web APIs com Python
  3. O que fazer com os dados?
  4. Publicando web APIs com Python e Flask.

#python #flask #sanic #flasgger


http://github.com/rochacbruno/flasgger




por Bruno Rocha em 29 de March de 2017 às 00:58

March 03, 2017

Magnun Leno

Hack ‘n’ Cast v1.5 - Contribuindo com o Mundo FOSS

Se você já desejou contribuir com o mundo do Free or Open Source Software mas nunca se sentiu capaz, esse episódio é pra você!

Baixe o episódio e leia o shownotes

por Magnun em 03 de March de 2017 às 03:30

March 02, 2017

Aprenda Python

Como passar conteúdo para o Javascript?

**tl;dr;** envie conteúdo pelo atributo `data-*` do HTML e pegue o resultado no Javascript com `getAttribute()`. Essa dúvida é muito comum para quem está começando a programar para web. Nosso cenário de exemplo é uma aplicação Django que gera uma página HTML completa, sem ajax, e precisa mandar um código de produto para o Javascript. Vou mostrar de maneira bem objetiva como "passar" o

por Vinicius Assef (noreply@blogger.com) em 02 de March de 2017 às 18:39

February 26, 2017

Aprenda Python

Simplificando comandos do git com alias

**tl;dr**: use _aliases_ do git para simplificar comandos complicados e digitar menos. Quem usa `git`, sabe que ele é muito poderoso, mas alguns comandos são complicados e nada intuitivos. Se você for como eu, sempre precisa dar uma conferida no manual antes de, por exemplo, desfazer um commit. Talvez isso seja uma característica planejada. Afinal, você precisa ter certeza antes de destruir

por Vinicius Assef (noreply@blogger.com) em 26 de February de 2017 às 02:33