Planeta PythonBrasil

PythonBrasil[10]

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 24, 2017

Rodolpho Eckhardt

Agora eu me tornei o destruidor de mundos

Entre os dias 5 e 11 de outubro eu estive em Belo Horizonte para participar da PythonBrasil 13. Entre rever amigos, fazer novos e participar de excelentes discussões, tive a honra de apresentar um keynote aos mais de 570 participantes. Tenho que agradecer muito à organização da PythonBrasil pelo convite e pela oportunidade de apresentar e conversar com uma das minhas comunidades favoritas. A responsabilidade de subir no palco se tornou ainda maior após a publicação da grade de palestras.

24 de October de 2017 às 20:07

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 13, 2017

Thiago Avelino

Great blogpost, congratulations on the superaction…

Great blogpost, congratulations on the superaction…

Whenever you need to talk (about any subject) I’m here.

por Avelino em 13 de August de 2017 às 20:53

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 27, 2017

Thiago Avelino

Parabéns Lucas, você é fera cara.

Parabéns Lucas, você é fera cara. É extremamente importe mudar e encerrar ciclos, nosso vida é feita por ciclos.

Não sei se chegou ver esse blogpost que escrevi sobre encerrar ciclos, acho legal ler: https://medium.com/@avelino0/encerrando-ciclos-viva-no-presente-n%C3%A3o-se-paralise-pelo-passado-a6ec11a62993

por Avelino em 27 de July de 2017 às 05:40

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 28, 2017

Blog do PauloHRPinheiro

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

June 16, 2017

Thiago Avelino

The legal robot and Artificial Intelligence for law!

Much has been said about robots that can read contracts. And the first image that comes to mind is this:

This is something very far and distant, complex and expensive. Right?

Not for Nuveo. We simplify the process!

Contrary to popular belief, this technology is much closer to our lives than it seems. Nuveo's software optimizes the work of lawyers or whoever needs to review contracts and track dates and milestones.

A 200-page contract can be summarized to 5 pages, with the details that really matter and need to be reviewed by the team – making sure that the review is taken on things that truly deserve attention.

Therefore, a review job that would take days or even weeks can be summed up in a few hours.

Do you want to know more about Nuveo? Talk to us!


The legal robot and Artificial Intelligence for law! was originally published in Nuveo on Medium, where people are continuing the conversation by highlighting and responding to this story.

por Avelino em 16 de June de 2017 às 00:19

June 04, 2017

Thiago Avelino

Conheça seu ambiente de trabalho

Aprender novas tecnologias (nesse caso linguagens de programação) não é um trabalho fácil, vai muito além de conhecer syntax da linguagem. Exige alguns desafios como entender porque a linguagem foi criada (existe), entender o eco sistema, como fazer deploy, como gerenciar ambiente de produção e etc.

Mantenha seu ambiente de trabalho organizado e com tudo a seu alcance

Pensando nessa introdução vou falar um pouco sobre conhecer o seu ambiente de trabalho antes de conhecer novas tecnologias (linguagens), é muito comum ver desenvolvedores querer usar sempre o que existe de mais novo no mercado (linguagem, framework, plugin, editor e etc) sem pensar no time de desenvolvimento envolvido, mas sera que isso realmente é a melhor coisa a se fazer para empresa que você trabalha? O nível de complexidade são diferente para pessoas, é extremamente comum ver um desenvolvedor (geralmente auto de data) aprender novas tecnologias muito rápido, mas nem todos são assim. Um dos trabalhos de ser CTO é saber dosar a dose do remédio para que todos os desenvolvedores esteja sempre na mesma página, isso envolve dizer alguns não momentâneos (não é fácil esse papel, mas é extremamente necessário).

Conhecer seu editor

É primordial você conhecer o editor que você trabalha, não adianta você querer usar o editor que esta em hype para a tecnologia X, use o que você realmente sabe usar. Principalmente na área de desenvolvimento de software muitos desenvolvedores fala de Emacs e Vim, você realmente precisa usar esses editores? Minha opinião é que você conheça, pois é importante conhece como outros desenvolvedores trabalha, mas leve isso como um novo caso de estudo não leve em paralelo com o estudo sobre a tecnologia que esta aprendendo, tenha foco no que você quer aprender, tempos tempo para aprender muita coisa na vida, basta ter paciência, perseverança e foco nos objetivos.

Pontos importantes para você conhecer do seu editor:

  • Teclas de atalhos: com o passar do tempo você percebe que seu dia começa ficar mais produtivo quando começa largar a dependência do mouse;
  • Uso de busca em código (não se prenda dentro do editor, conheça como fazer usando bash também);
  • Trabalhar com mais de um projeto na mesma janela do Editor (Hoje é extremamente comum dividir o que é frontend e backend, trabalhar com micros serviço e etc, com esse cenário sera necessário você se adaptar em trabalhar com mais de um projeto aberto pois você precisa implementar e/ou corrigir código em mais de um projeto), as maioria dos editores de hoje em dia lhe da suporte a isso, fique tranquilo;
  • Conhecer configuração do seu editor, como colocar um plugin novo, como configurar uma linguagem nova e etc.
Usei durante muitos anos TextMate até me dedicar a aprender VIM (sim, parei de estudar durante 6 mês outras tecnologias e me voltei para o estudo do VIM), isso me fez aprender muito mas é uma escolha, o que você quer aprender? X ou Y? Depois de anos usando VIM resolvi aprender usar Emacs e já estou a 2 anos usando ele como editor principal, ou seja, mudamos de opniões e temos disponibilidade de conhecer coisas novas.

Formas de debugar seu software

Saber uma linguagem de programação nova não basta saber a syntax e sim quais ferramentas ela lhe traz para debugar o software que esta desenvolvendo, seja print, breakpoint, gdb, pdb ou qual quer outra forma. Como desenvolvedor é extremamente importante software ser capaz de achar bugs dentro do software que você esta desenvolvendo ou dando manutenção (desenvolvido por outro desenvolvedor), se você não estudar a fundo como debugar software na sua linguagem você tera alguns problemas para achar o bug que seu software pode ter.

Logs, ambiente de produção precisa ser rastreável

Antes de falar de logs você precisa ter em mente as principais diferença de logprint.

Conhecer uma linguagem de programação nova é necessário saber como ela se comporta em produção (falando mais uma vez, não é só saber syntax), logar eventos do seu software é extremamente importante, colocar cor para identificar evento é muito importante para você não perder horas tendo que analisar um log, ou até mesmo jogar os logs coletados fora pois esta impossível de se fazer analise (infelizmente já vi mais de uma vez isso acontecendo).

Pratique (treine)

Todos atleta de alta performance precisa praticar (exaustivamente) para ficar bom o suficiente para assim competir. Em desenvolvimento de software não é diferente, você precisa praticar para conseguir desenvolver software sem ficar pensando "qual a teclada de atalho no meu editor mesmo?", "qual o nome daquele pacote que faz XYZ mesmo?" e etc. Se você tiver que ficar pensando nessas coisas como seu cérebro tem espaço pra saber qual o problema que você tem que resolver, pensar na melhor maneira da sua arquitetura? Não tem como pensar nessas coisas pois seu cérebro está concentrado em entender seu eco sistema e não resolver o problema, por isso devemos ter o eco sistema em nossa mente para ai sim depois pensar na solução do problema.

Quanto melhor você conhecer suas ferramentas melhor você será como desenvolvedor. Uma forma que eu faço para praticar é contribuir com projetos open source, desenvolver não é apenas escrever código e contribuição open source envolve mais relacionamento humana (remoto que é mais difícil ainda) do que código.

Se você não é bom em falar com pessoal é importante aprender by JavaMan

Considerações finais

Entenda seu ambiente de trabalho, você precisa dominar seu eco sistema para performar mais no seu dia a dia, deixe ele tão simples para o seu cérebro para você não precisar pensar mais nele. Trabalhe com uma tecnologia que lhe deixe feliz e faça com ela o que você realmente precisa fazer, não se engane buscando desculpas dentro de si mesmo para lhe justificar o que esta fazendo (aprendendo X ou Y). Mantenha seu foco em estudos que lhe traz evolução profissional e/ou pessoal.

por Avelino em 04 de June de 2017 às 17:16

May 27, 2017

Thiago Avelino

Parabéns pelos 10 anos de empresa Michel, não é nada fácil passar do primeiro ano de vida imaginei…

Parabéns pelos 10 anos de empresa Michel, não é nada fácil passar do primeiro ano de vida imaginei chegar aos 10 anos como vocês estão. Sucesso e que daqui 10 ano eu esteja lendo o blogpost de 20 anos…

por Avelino em 27 de May de 2017 às 16:54

May 26, 2017

Thiago Avelino

Browser Automation to query public base and automation of work

Following our articles talking about Nuveo products, today we want to highlight "Query".

But what is Nuveo Query anyway?

Query is nothing more than a sequence of jobs and searches on different web, internet databases run by the Nuveo Robot.

The product was built through a partnership between Nuveo and MCamilo (a Brazilian BPO company), after identifying some great opportunities in the insurance market.

In summary, Query is ready to perform any repetitive web search work and bring relevant data about people or companies according to their configuration… and then include them in a database, report or specific format defined by the customer that uses the product.

The first robot moment frightens, but it came to expedite the work

Let’s look at a real life example: imagine that a bank needs certain information before giving a loan to a company. The bank will need to search, extract and validate several pieces of information, of that company, in different databases or websites. Now you just have to configure the Nuveo software according to a certain schedule, define what information needs to be validated and Query — the Nuveo Robot software — will do the rest… and bring it all ready for a last check only. That happens in a few seconds. Isn´t that incredible?

Do you see Query running in your business? Contact us.


Browser Automation to query public base and automation of work was originally published in Nuveo on Medium, where people are continuing the conversation by highlighting and responding to this story.

por Avelino em 26 de May de 2017 às 16:01

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 23, 2017

Thiago Avelino

Application of artificial intelligence in the financial market, in Brazil

Artificial Intelligence has a number of financial applications and we’re going to talk about it them.

Boost is nothing more than an optimization of reconciliation processes made in the Financial area.

In Brazil, Nuveo developed a solution for the department that generates most hidden costs to the company: payables and receivables.

Can you believe that the average rate charged by credit card companies is around 6%, for online trading? That’s right… and that´s aside from Chargebacks (Fraudulent Operations).

As an industry entrepreneurs and founders of a payment solutions company, we always tried to avoid these costs all the time, creating innovative alternatives to serve people that don’t have bank accounts and reducing our costs in the process as a whole.

The most common alternative, and that many of the small business owners use, is the so-called Banking Deposit. This is just the process of informing your bank account to your customer, asking for a wire transfer, and then requesting the payment slip. The next steps are to reconcile the payment and deliver the product or service.

What is the big problem in here? It’s how scalable the process is.

Imagine that a simple operation that has 10, 15 transactions per day. It is very easy to have an person running the reconciliation and delivering orders.

What if the business runs thousands of transactions per day, how many people will it take to make such the process?

Nuveo created a system that automatically reconciles customers’ slip images and the company´s bank statements. The robot interprets the image information and matches it with your bank statement, releasing the customer’s order through the ERP system.

With this technology, it is now possible to automate the deposit reconciliation process and significantly reducing operating costs — and also serving a wider variety of customers, mainly the ones who do not have bank accounts.

Do you see Nuveo working for you? Contact us!


Application of artificial intelligence in the financial market, in Brazil was originally published in Nuveo on Medium, where people are continuing the conversation by highlighting and responding to this story.

por Avelino em 23 de May de 2017 às 01:48

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 21, 2017

Allison Azevedo

python-vindi

Acabei de subir o primeiro release (0.1.0) do python-vindi, esse é o primeiro projeto que usa o python-simple-rest-client como base (Python 3.5+).

Github: https://github.com/allisson/python-vindi


por Allisson Azevedo em 21 de April de 2017 às 15:57

April 18, 2017

Allison Azevedo

python-simple-rest-client

Acabei de subir o primeiro release (0.1.0) do python-simple-rest-client, essa lib necessita do python 3.5+ e no próximo release vai vir com suporte ao asyncio (via aiohttp).

Github: https://github.com/allisson/python-simple-rest-client

Documentação: http://python-simple-rest-client.readthedocs.org/


por Allisson Azevedo em 18 de April de 2017 às 15:29

Programação Assíncrona com Asyncio

No dia 25/03/2015 eu tive a oportunidade de apresentar a palestra Programação Assíncrona com Asyncio no PythonDay Campina Grande, o vídeo, slides e código estão disponíveis logo abaixo.

Vídeo:

Slides: http://allisson.github.io/slides/2017/pythonday_cg/
Códigos de exemplo: https://github.com/allisson/pythonday-campina-grande-2017


por Allisson Azevedo em 18 de April de 2017 às 15:20

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 14, 2017

Diego Garcia

Criando Um Aplicativo De Linha De Comando Com Python

Com certeza você já uso algum aplicativo de linha de comando, seja dos mais simples (como por exemplo o echo), ou dos mais sofisticados (como é o caso do cURL). O fato é, todo programador deveria criar pelo menos uma vez um aplicativo de linha de comando, seja para fins de estudo, ou até mesmo para se tornar uma grande ferramenta. Com python, criar aplicativos de linha de comando é algo muito simples e produtivo.

Hello World

Antes de nos aprofundarmos no assunto, faremos um simples Hello World utilizando o módulo argparse que é built-in do python.

# cli.py
from argparse import ArgumentParser


parser = ArgumentParser()
parser.add_argument('name', help='say your name')

args = parser.parse_args()
print('Hello {}'.format(args.name))

Resumindo o código anterior, criamos um parser e nesse parser adicionamos um argumento posicional (o argumento name). Depois convertemos os argumentos da linha de comando no objeto args e mostramos uma mensagem utilizando o valor passado para o argumento name. O resultado será o seguinte:

$ python cli.py World
Hello World

Pode parecer mais complicado do que simplesmente recuperar os valores de sys.argv, porém, é algo muito mais poderoso. Um exemplo disso é o fato de que, esse código da forma como está, já possui um help bem intuitivo dos possíveis comandos aceitos:

$ python cli.py -h
usage: cli.py [-h] name

positional arguments:
  name        say your name

optional arguments:
  -h, --help  show this help message and exit

Um pouco sobre o Argparse

O argparse é um módulo que foi adicionado a standard library do python a partir da versão 2.7 substituindo seu antecessor, o módulo optparse. Ele foi projetado para criar aplicativos de linha de comando de forma amigável e descomplicada.

O parser

O principal componente do módulo argparse é a classe ArgumentParser. A partir de uma instância de ArgumentParser é possível determinar o comportamento de linha de comando do aplicativo. Na criação do parser é possível informar alguns parâmetros relativos ao aplicativo, sendo que alguns deles servem para customizar a mensagem de help gerada automaticamente, como é o caso do parâmetro description:

>>> from argparse import ArgumentParser
>>> parser = ArgumentParser(description='Powerful command-line tool')
>>> parser.parse_args(['-h'])
usage: [-h]

Powerful command-line tool

optional arguments:
  -h, --help  show this help message and exit

Note que é possível informar para o parser os argumentos que ele deve parsear, através do método parse_args. Esse método espera uma lista de strings como parâmetro e caso essa não seja informada, recupera a lista de argumentos através do sys.argv, ou seja, os argumentos de linha de comando.

Argumentos

Com vimos no primeiro exemplo, é possível adicionar argumentos ao parser, sejam eles posicionais (e obrigátorios), ou opcionais.

Argumentos posicionais

Os argumentos posicionais, são argumentos obrigatórios que devem ser informados na ordem em que foram declarados. Veja um exemplo de uma simples soma feita através da linha de comando:

# cli.py
from argparse import ArgumentParser


parser = ArgumentParser()
parser.add_argument('first_number', type=int)
parser.add_argument('second_number', type=int)

args = parser.parse_args()
print('{} + {} = {}'.format(
    args.first_number,
    args.second_number,
    args.first_number + args.second_number
))

O exemplo de uso do código anterior seria algo como:

$ python cli.py 2 5
2 + 5 = 7

Não se preocupe com o type no exemplo, veremos o que isso significa mais adiante

Argumentos opcionais

Os argumentos opcionais são declarados com um ou dois hífens no prefixo do nome (e.g. -f, --foo) e não dependem de uma posição especifica para serem informados. Usando o exemplo anterior, vamos adicionar um argumento opcional para determinar se o output do comando deve ou não ser verboso:

# cli.py
from argparse import ArgumentParser


parser = ArgumentParser()
parser.add_argument('first_number', type=int)
parser.add_argument('second_number', type=int)
parser.add_argument('--verbose', action='store_true')

args = parser.parse_args()
result = args.first_number + args.second_number
if args.verbose:
    print('{} + {} = {}'.format(
        args.first_number,
        args.second_number,
        result
    ))
else:
    print(result)

Desta forma, possibilitamos saidas diferentes da nossa aplicação de acordo com a presença ou não do argumento --verbose.

$ python cli.py 2 5
7
$ python t.py 2 5 --verbose
2 + 5 = 7

Não se preocupe com o action no exemplo, veremos o que isso significa mais adiante

Podemos simplificar aindas mais o argumento --verbose criando uma opção encurtada dele. O método add_argument do ArgumentParser aceita uma lista de nomes do argumento, sendo assim, basta adicionar as opções na criação do argumento:

parser.add_argument('-v', '--verbose', action='store_true')

E inclusive, essa alteração já reflete no help da aplicação:

$ python cli.py -h
usage: cli.py [-h] [-v] first_number second_number

positional arguments:
  first_number
  second_number

optional arguments:
  -h, --help     show this help message and exit
  -v, --verbose

Vale ressaltar que essa opção só faz sentido para argumentos opcionais.

Types

Por padrão, o valor de todos os argumentos são interpretados como string, porém, é possível determinar um tipo para esses valores, através do parametro type do método add_argument como já vimos anteriormente.
Ao determinar um type para um argumento, será executado um type-checking no momento do parse_args() para garantir que o valor do argumento é do tipo especificado:

>>> from argparse import ArgumentParser
>>> parser = ArgumentParser()
>>> parser.add_argument('number', type=int)
>>> args = parser.parse_args(['a'])
usage: [-h] number
: error: argument number: invalid int value: 'a'

Qualquer callable pode ser usado como type, pontencializando ainda mais o uso de tipos nos argumentos, e.g.

# cli.py
import argparse


def odd(n):
    n = int(n)
    if n % 2 == 1:
        return n
    raise argparse.ArgumentTypeError('{} is not an odd number'.format(n))


parser = argparse.ArgumentParser()
parser.add_argument('odd_number', type=odd)
args = parser.parse_args()

Neste exemplo, se pasarmos um numero par para o argumento odd_number teremos uma mensagem de erro indicando que esse numero não é um numero impar.

$ python cli.py 2
usage: [-h] odd_number
: error: argument odd_number: 2 its not an odd number

Actions

É possível determinar ações para determinados argumentos sendo a mais comum a ação store que basicamente armazena o valor passado para o argumento. Esta é a ação default para qualquer argumento caso outro tipo de ação não seja informado.
Existem outros tipos de ações, veremos algumas delas.

store_true

A action store_true basicamente armazena o valor True caso o argumento seja informado (como vimos anteriormente no exemplo do argumento --verbose).

append

A action append armazena uma lista dos valores passados para o mesmo argumento, e.g.:

>>> from argparse import ArgumentParser
>>> parser = ArgumentParser()
>>> parser.add_argument('--file', action='append')
>>> parser.parse_args('--file 1.txt --file 2.txt --file 3.txt'.split())
Namespace(file=['1.txt', '2.txt', '3.txt'])

count

A action count armazena um contador de vezes em que um argumento foi usado (útil para determinar niveis de verbosidade), e.g.:

>>> from argparse import ArgumentParser
>>> parser = ArgumentParser()
>>> parser.add_argument('--verbose', '-v',  action='count')
>>> parser.parse_args(['-vvvv'])
Namespace(verbose=4)

Casos mais complexos

Normalmente, no desenvolvimento de um aplicativo cli acabamos tendo que lidar com casos mais complexos, e não somente um comando simples com alguns parâmetros. Esses aplicativos tendem a crescer e lidar com outros subcomandos, como por exemplo o git (add, commit, branch, etc):

Iremos criar um aplicativo simples com dois subcomandos, o subcomando read e o subcomando write.

A melhor maneira de lidar com subcomandos é criar SubParsers especificos para cada comando que o aplicativo irá lidar.

SubParsers

Subparsers são parsers independentes, com suas próprias caracteristicas mas que deviram de um parser principal. Vamos começar nosso aplicativo de exemplo criando seu parser principal e seus dois subparsers:

# cli.py
from argparse import ArgumentParser


parser = ArgumentParser()

subparsers = parser.add_subparsers()
r_parser = subparsers.add_parser('read', help='Commands to read data')
w_parser = subparsers.add_parser('write', help='Commands to write data')

parser.parse_args()

Se dermos uma olhada no help gerado pelo python, já veremos instruções de uso para os subcomandos:

$ python cli.py -h
usage: cli.py [-h] {read,write} ...

positional arguments:
  {read,write}
    read        Commands to read data
    write       Commands to write data

optional arguments:
  -h, --help    show this help message and exit

Iremos agora incrementar um pouco mais nosso aplicativo adicionando argumentos para cada subcomando:

# cli.py
from argparse import ArgumentParser

parser = ArgumentParser()
subparsers = parser.add_subparsers(help='Sub commands')

r_parser = subparsers.add_parser('read', help='Commands to read data')
r_parser.add_argument('origin', help='File origin')
r_parser.add_argument('--head', type=int, default=0, help='Read only N lines')

w_parser = subparsers.add_parser('write', help='Commands to write data')
w_parser.add_argument('destination', help='Destination file')
w_parser.add_argument(
    '--upper',
    action='store_true',
    help='Write all in UPPERCASE'
)

if __name__ == '__main__':
    args = parser.parse_args()

Agora é possível ter um help geral do aplicativo e um help para cada subcomando:

$ python cli.py -h

usage: cli.py [-h] {read,write} ...

positional arguments:
  {read,write}  Sub commands
    read        Commands to read data
    write       Commands to write data

optional arguments:
  -h, --help    show this help message and exit
$ python cli.py read -h

usage: cli.py read [-h] [--head HEAD] origin

positional arguments:
  origin       File origin

optional arguments:
  -h, --help   show this help message and exit
  --head HEAD  Read only N lines
$ python cli.py write -h

usage: cli.py write [-h] [--upper] destination

positional arguments:
  destination  Destination file

optional arguments:
  -h, --help   show this help message and exit
  --upper      Write all in UPPERCASE

set_defaults

Um problema ao se utilizar subparsers é que ao executar o parser.parser_args() não é possível determinar qual subcomando foi requisitado, somente os argumentos do subparser:

$ python cli.py read foo.txt --head 2
Namespace(head=2, origin='foo.txt')

Para contornar esse comportamento, é possível determinar valores default para um subparser através do método set_default, que espera um conjunto de argumentos nomeados (**kwargs):

# cli.py
r_parser.set_defaults(command='read')
...
args = parser.parse_args()
print('subcommand: ', args.command)

Para o exemplo anterior, ao chamar o cli.py na linha de comando passando o subcomando read teriamos a seguinte saida:

$ python cli.py read foo.txt
subcommand: read

Criando handlers para os subparsers

Conhecendo esse truque e sabendo que o valor default pode ser de qualquer tipo (inclusive um callable), podemos criar handlers para os nossos subcommands.
Vamos mudar nosso código de exemplo adicionado duas funções, a função read e a função write:

def read(args):
    print('call read with: {}'.format(args))


def write(args):
    print('call write with: {}'.format(args))

Agora iremos usar essas funções como um valor default em nossos subparser:

r_parser.set_defaults(handler=read)
w_parser.set_defaults(handler=write)

E por fim, iremos usar essas funções após a leitura dos argumentos da linha de comando:

args = parser.parse_args()
args.handler(args)

Ao executarmos nosso cli passando um subcomando, podemos ver que a saída do comando indica que nosso handler foi chamado:

$ python cli.py write foo.txt --upper
call write with: Namespace(destination='foo.txt', handler=<function write at 0x100de9140>, upper=True)

Isso abre a possíbilidade de criar aplicativos realmente complexos de forma simples e organizada.

Testando Parsers

“I don’t care if it works on your machine! We are not shipping your machine!” — Vidiu Platon.

Software sem testes é um software que não deve ser entregue, ou seja, é um software incompleto. Já sabemos como criar complexas soluções de aplicativos de linha de comando, só nos falta saber como criar testes para essa aplicações.
Usando o ArgumentParser do python não existe muitos mistérios em como realizar os testes, para utilizar o método parse_args() e verificar a forma como os argumentos foram processados:

>>> from argparse import ArgumentParser
>>> parser = ArgumentParser()

>>> subparser = parser.add_subparsers()
>>> x_parser = subparser.add_parser('x')
>>> x_parser.set_defaults(foo='bar')

>>> args = parser.parse_args(['x'])
>>> assert args.foo == 'bar'
>>> assert args.foo == 'x'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

Algumas quebras de linha foram adicionadas para melhorar a leitura

Você pode tranquilamente utilizar esse recurso dentro da sua suite de testes.

Distribuindo seu aplicativo

Com o aplicativo já escrito (e devidamente testado), chegou a hora de distribuir para o mundo. Não irei entrar em detalhes sobre como prepara o arquivo setup.py de forma correta (talvez em outro post), porém irei comentar sobre uma configuração especifica, os entry_points.

Entrypoint

Existem diferentes tipos de entrypoints disponíveis para serem usados, em nosso exemplo iremos usar o mais comum deles, o console_script, com isso, iremos determinar como a nossa aplicação deverá ser chamada na linha de comando após a instalação:

# setup.py
from setuptools import setup, find_packages

author_name = 'Diego Garcia'
author_email = 'drgarcia1986@gmail.com'

setup(
    name='cli',
    version='0.0.1',
    description='Cool cli',
    long_description='Cool cli from http://www.diego-garcia.info/,
    url='https://github.com/drgarcia1986/cli',
    author=author_name,
    author_email=author_email,
    maintainer=author_name,
    maintainer_email=author_email,
    license='MIT',
    classifiers=[
        'Development Status :: 4 - Beta',
        'Intended Audience :: Developers',
        'Topic :: System :: Shells',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3.3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
    ],
    keywords='cli',
    download_url='https://github.com/drgarcia1986/cli/archive/master.zip',
    packages=find_packages(exclude=['tests*']),
    install_requires=[],
    entry_points={'console_scripts': ['cli = cli:main']},
    platforms='windows linux',
)

O detalhe importante fica por conta da linha entry_points={'console_scripts': ['cli = cli:main']}, onde determinamos que o entry point de console script da nossa aplicação, será a função main dentro do arquivo cli, para quando a aplicação for chamada como cli na linha de comando.
Essa função main nada mais é do que uma função que chama o método parser_args(), e.g.:

def main():
    args = parser.parse_args()
    args.handler(args)

Pronto, ao realizar a instalação da aplicação podemos utiliza-la na linha de comando apenas chamando o comando cli:

$ cli -h

usage: cli [-h] {read,write} ...

positional arguments:
  {read,write}  Sub commands
    read        Commands to read data
    write       Commands to write data

optional arguments:
  -h, --help    show this help message and exit

Third party libs

Existem algumas bibliotecas opensource que podem facilitar a vida de quem pretende escrever um aplicativo de linha de comando, vou listar algumas:

Você pode testá-las e concluir se alguma se encaixa melhor no seu problema ou se a standard library do python já é o suficiente.

Referências
Argparse Official Documentation
Argparse tutorial
Python Argparse Cookbook
argparse – Command line option and argument parsing

por Diego Garcia em 14 de March de 2017 às 01:00

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

March 01, 2017

Thiago Avelino

Parabéns pelo post inspirador Le.

Parabéns pelo post inspirador Le.

Sair da zona de conforto realmente não é fácil, mas super gratificante quando paramos para analisar pós turbulência.

por Avelino em 01 de March de 2017 às 19:21

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

February 24, 2017

Aprenda Python

Diretório __pycache__ e arquivos .pyc

Quem nunca se incomodou com os diretórios `__pycache__` criados pelo Python? Ou com os arquivos `.pyc` criados pelo Python legado (Python 2)? Eles incomodam, mas para que servem? Os diretórios `__pycache__` guardam os arquivos `.pyc`, que são a versão compilada dos módulos importados por seu programa. Sendo bem simplista, o Python verifica se já existe uma versão no formato `.pyc` antes de

por Vinicius Assef (noreply@blogger.com) em 24 de February de 2017 às 20:29

February 16, 2017

Magnun Leno

February 03, 2017

Humberto Rocha

Desbravando o pygame 2 - Desenhando na Tela

Ao longo de um jogo estamos constantemente desenhando na tela. E agora que já sabemos criar um programa em pygame é hora de começar a desenhar.

Plano de desenho

Voltando nas aulas de matemática do ensino fundamental fomos apresentados ao plano cartesiano. O plano cartesiano e um espaço bidimensional orientado pelos eixos x e y onde x aumenta para o lado direito e diminui para o lado esquerdo enquanto y aumenta para cima e diminui para baixo:

plano cartesiano

No pygame a coisa muda um pouco, o plano de desenho consiste também de dois eixos onde x aumenta para a direita e diminui para a esquerda, entretanto o y aumenta para baixo e diminui para cima, o inverso do plano cartesiano, sendo a área visível da tela um espaço que parte de do ponto x=0 e y=0 no topo esquerdo da tela até o tamanho determinado no comando pygame.display.set_mode((x, y)):

plano de desenho

Desenhando

Crie um arquivo draw.py com o seguinte conteúdo:

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

import time

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))
# carregando fonte
font = pygame.font.SysFont(None, 55)

pygame.display.set_caption('Olá mundo')

# preenchendo o fundo com preto
screen.fill(BLACK)

# desenhando na superfície 
pygame.draw.line(screen, WHITE, [10, 100], [630, 100], 5)
pygame.draw.rect(screen, BLUE, [200, 210, 40, 20])
pygame.draw.ellipse(screen, RED, [300, 200, 40, 40])
pygame.draw.polygon(screen, GREEN, [[420, 200], [440, 240], [400, 240]])

# atualizando a tela
pygame.display.flip()

time.sleep(5)

# preenchendo o fundo com preto
screen.fill(BLACK)

# definindo o texto
text = font.render('pygame', True, WHITE)
# copiando o texto para a superfície
screen.blit(text, [250, 200])

# atualizando a tela
pygame.display.flip()

time.sleep(5)

Iniciamos nosso programa definindo os valores de cores para serem usados mais a frente. Em seguida inicializamos o pygame como feito anteriormente e carregamos uma fonte com pygame.font.SysFont para ser usada para escrever na tela.

Após preencher a superfície screen com a cor preta utilizamos algumas funções de desenho disponíveis em pygame.draw:

  • line: desenha uma linha recebendo como parâmetros a superfície seguida da cor, uma lista com a coordenada de início e e uma lista com a coordenada de fim e a espessura da linha em pixels.
  • rect: desenha um retângulo recebendo como parâmetros a superfície seguida da cor e uma lista com a posição inicial em x, posição inicial em y, largura, altura.
  • ellipse: desenha uma circunferência recebendo como parâmetros a superfície seguida da cor e uma lista com a mesma estrutura do rect.
  • polygon: desenha um polígono recebendo como parâmetros a superfície seguida da cor e uma lista de listas com as coordenadas dos vértices do polígono.

Em seguida pygame.display.flip() atualiza toda a tela o conteúdo desenhado previamente na superfície screen, seguido de um intervalo de 5 segundos.

Passado o tempo do sleep, preenchemos a superfície com a cor preta e definimos o texto que será escrito na tela com font.render, passando como primeiro parâmetro a string pygame, em segundo o valor True indicando para usar antialias que suaviza o contorno do texto e em terceiro o branco como cor. Este método cria uma superfície intermediária com o texto que será passado para nossa superfície screen através do método blit que recebe a superfície que será copiada e seu posicionamento em screen.

Por fim, atualizamos a tela com o novo conteúdo, esperamos mais 5 segundos e finalizamos nosso programa.

Concluindo

Existem diversas formas de se desenhar na tela com pygame, você pode ver estes e outras funções de desenho com mais detalhes na documentação da biblioteca.

O código deste capítulo encontra-se disponível no repositório desbravando-pygame.

por Humberto Rocha em 03 de February de 2017 às 00:00

February 02, 2017

Magnun Leno

January 30, 2017

Lauro Moura

C# – sizeof vs Marshal.SizeOf

Depois do PySide – ainda no INDT como já falei em outros posts antigos – e dos bindings JS para o EFL, no meu trabalho atual estamos fazendo bindings para C#, mais especificamente para o Mono.

A princípio é relativamente simples usar código C a partir de C#. De forma resumida, basta declarar uma função em C# dizendo de que biblioteca ele deve importar a função nativa – pense em dlopen/dlsym – e então invocar a função. O Mono cuida de converter os tipos entre o código gerenciado e a função nativa, tanto os parâmetros como o retorno da função. Por exemplo, direto do guia do Mono para interoperabilidade:

[DllImport ("libc.so")]
private static extern int getpid ();

Lógico que isso é apenas o caso mais simples. Dependendo das peculiaridades do tipo a ser convertido, você pode precisar colocar mais informações para orientar o Mono nessa conversão, como o layout das estruturas, o formato de conversão de strings, ou mesmo uma conversão customizada.

Numa dessas customizações tive problemas durante a chamada de algumas funções, onde misteriosamente a pilha de chamada estava sendo corrompida. Depois de alguns testes, vi que as funções que corrompiam os dados envolviam uma estrutura que era passada por valor como argumento. De forma análoga às funções, onde a assinatura que você declara em C# é uma cópia da assinatura nativa e representa o “layout” daquela função na memória, com as estruturas você também faz o mesmo em C#. No caso, essa estrutura era declarada manualmente em C# da seguinte forma:

struct FooBar {
  IntPtr obj;
  bool something;
  bool another_thing;
  int size;
}

Enquanto que em C a estrutura tinha o seguinte formato:

struct Foo_Bar {
  Obj *obj;
  byte something : 1; // Na pratica é um typedef p/ byte
  byte another_thing : 1;
  int size;
}

A princípio tudo parece correto, já que bool no C# é armazenado no espaço de 1 byte, e apesar do bit field em C, cada field “byte” no C ocupava também 1 byte no final, devido ao packing da estrutura.

Ao realizar mais testes, inicialmente usando sizeof no C# e no C, o tamanho e os offsets dos campos estavam iguais entre o C# e C. Foi então que entrou em cena do Marshal.SizeOf. Marshal é uma classe do C# responsável por cuidar da conversão (marshalling) de tipos entre o código gerenciado e o código nativo.

O problema com sizeof era que ele media o uso de memória gerenciada dos tipos. E, curiosamente, o tipo booleano de C# por padrão difere no espaço utilizado entre a memória gerenciada (1) e memória nativa (4), este último corretamente informado pelo Marshal.SizeOf. A solução então foi indicar para o compilador para usar apenas 1 byte ao converter os campos booleanos, da seguinte forma:

struct FooBar {
  IntPtr obj;
  [MarshalAsAttribute(UnmanagedType.U1)] bool something;
  [MarshalAsAttribute(UnmanagedType.U1)] bool another_thing;
 int size;
}

Feito isso, o problema foi corrigido e todos viveram felizes até o próximo bug. 🙂


por lauro em 30 de January de 2017 às 02:43

January 21, 2017

JungleCoders

Migrando o servidor de chat para Python 3.6

Na época do lançamento do Python 3.4, eu estava tão contente com a integração do Asyncio que escrevi um servidor de chat aqui. O tempo passou e novas versões do Python foram lançadas. Resolvi então migrar o servidor para Python 3.6.

Uma das grandes mudanças que ocorreram no Python 3.5, foi o suporte a async e await para substituir @asyncio.corroutine e yield from respectivamente. Esta pequena mudança por si só já facilita em muito a leitura do código, que ficou mais leve. Mas uma das principais mudanças do Python 3.6 são as f-strings que facilitam a formação de mensagens.

Primeiro, vamos preparar o ambiente. É preciso instalar o Python 3.6. Se você utiliza Windows, basta baixar o pacote no site da Python.org.

Ubuntu 16.10

Se você utiliza Ubuntu 16.10, ainda precisa baixar os fontes e compilar... mas seguindo a recomendação de amigos do Telegram, resolvi experimentar com o pyenv!

Para instalar no Ubuntu, baixe o install_python360.sh e rode com:
bash install_python360.sh


Como alguns pacotes precisam ser instalados no Ubuntu, ele vai usar sudo. Esteja pronto para digitar a senha. No meu caso, como uso docker (docker run -rm -t -i ubuntu:16.10 /bin/bash), rodei o script como root. Se você instalar no seu usuário, ele vai chamar o sudo quando necessário. Eu gravei um pequeno vídeo do que aconteceu na minha instalação:


Windows

Depois de instalar o Python 3.6.0, instale o websockets com pip3 install websockets

Outros sistemas

Instale o Python 3.6.0 e o módulo websockets.

O novo servidor


Mudando @asyncio.coroutine para async def, o código já fica mais claro. Em uma segunda passagem, eu substitui os yield from por await. Como estamos usando Python 3.6, não custa adaptar as strings para f-strings. E para terminar a migração, configurei o log para que o código não fique cheio de prints! Ficou assim:

Antes de executar, temos que preparar um certificado SSL (no Linux).

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes


O cliente


Hoje não tem como escapar do Javascript. Fiz poucas alterações no código, a maior delas foi simplesmente cosmética e agora o texto rola para baixo automaticamente quando novas mensagens chegam.


Rodando


Imaginando que você esteja no mesmo diretório dos arquivos deste post, vamos criar um servidor web simples com python, claro:
python -m SimpleHTTPServer 8080


Deixe rodando e abra um outro terminal. Vamos executar nosso servidor:
python server.py


E finalmente, abra o browser usando localhost ou seu ip:
http://localhost:8080/cliente.html

Observação: como utilizamos um certificado auto-assinado, precisamos dar permissão ao browser de abrir a página. Como o websocket apenas usa SSL, abra uma outra janela no browser, mas na porta 8765:
https://localhost:8765

Siga o procedimento de seu browser para abriar a página. Normalmente você deve clicar em um botão dizendo que quer continuar acessando a página. Se tudo der certo, você receberá a mensagem: Invalid request. Feche a janela e recarrege o cliente em:
http://localhost:8080/cliente.html

Ele agora deve ter conectado normalmente. Abra outra janela no mesmo endereço.
Digite:
/nome X

e depois envie uma mensagem. Ela deve aparecer na outra janela. Você deve digitar /nome Nome antes de enviar mensagens. Teste com vários clientes, modifique e divirta-se.







por Nilo Menezes (noreply@blogger.com) em 21 de January de 2017 às 19:19

January 16, 2017

PythonClub

Instalando o Python versão 3.7.0 alpha 1 no Ubuntu 16.04

Instalando o Python versão 3.7.0 alpha 1 no Ubuntu 16.04

A versão mais recente do Python, a 3.7.0 alfa 1, pode agora ser baixada ou clonada do GitHub facilmente. Uma das linguagens mais fáceis de usar e aprender, o Python foi criado nos anos 90 e é elogiado por sua fácil leitura de código e necessidade de poucas linhas de código, comparada a outras linguagens. Agora mais próxima da comunidade no Github!

Depois disso os caminhos mudaram e conheci a profissão de Analista de Suporte e me ocupo disso desde então. Atualmente voltei a aprender uma linguagem, antes de mais nada, dei uma atualizada em lógica de programação, por sinal existem muitas boas apostilas e cursos gratuitos na Internet, dois caminhos muito bons.

Sobre linguagem de programação, existem várias. Neste quesito comecei a conhecer a linguagem Python e logo me apaixonei pela simplicidade, beleza e eficiência.

Depois disso tudo, você tem que instalar a linguagem em sua máquina. Por padrão, o Ubuntu 16.04 instala a versão 3.4, mas se você quiser, pode usar a versão 3.7.0a0

Obs.: Execute os comandos como root, ou usando o comando sudo no terminal.

git clone https://github.com/python/cpython
cd cpython
apt-get install build-essential libssl-dev libffi-dev python3-dev
./configure
make
make test
make install

# Se você quiser usar várias versões do Python 2.7, 3.6 e 3.7 use o comando abaixo
make altinstall

Observação: via apt instalei as dependências do python, no caso o openssl, porque o pip apresenta vários problemas com certificados na instalação dos módulos, mas, isso é para outro artigo

Depois disso é só entrar no interpretador:

python3.7

Tela do interpretador Python

Python 3.7.0a0 (default, Feb 16 2017, 18:59:44) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

Referências

Para ler mais sobre a linguagem: Python - Site oficial da Linguagem Python! This is Python version 3.7.0 alpha 1 - Git da próxima versão do Python, hospedado no Github! * Python-Brasil - A comunidade Python Brasil reune grupos de usuários em todo o Brasil interessados em difundir e divulgar a linguagem de programação.

por Welton Vaz em 16 de January de 2017 às 22:37

Abrangência de Listas e Dicionários

A utilização de listas em Python é algo trivial. A facilidade provida pela linguagem aliada a simplicidade da estrutura de dados list a torna, ao lado dos dicionários dict, uma das estrutura de dados mais utilizadas em Python. Aqui neste tutorial irei compartilhar algo que aprendi trabalhando com listas e dicionário em Python, mais especificamente no que diz respeito a abrangência de listas (e dicionários).

Abrangência de listas

A abrangência de listas, ou do inglês list comprehensions, é um termo utilizado para descrever uma sintaxe compacta que o Python nos oferece para criamos uma lista baseada em outra lista. Pareceu confuso? Ok, vamos aos exemplos!

Exemplo 1

Vamos suport que temos a seguinte lista de valores:

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

Queremos gerar uma outra lista contendo o dobro de cada um desses números, ou seja,

[2, 4, 6, 8, 10]

Inicialmente, podemos montar o seguinte código como solução:

# Recebe o nosso resultado
valores_dobro = []

for val in valores:
    valores_dobro.append(val * 2)

print(valores_dobro)

>>>
[2, 4, 6, 8, 10]

A solução acima é uma solução simples e resolve nosso problema, entretanto para algo tão simples precisamos de 4 linhas de código. Este exemplo é uma situação onde a abrangência de lista pode ser útil. Podemos compactar a criação da lista valores_dobro da seguinte maneira:

valores_dobro = [valor*2 for valor in valores]

Bacana não? O exemplo seguinte podemos incrementar mais o exemplo acima.

Exemplo 2

Vamos supor que desejamos criar uma lista onde apenas os valores pares (resto da divisão por 2 é zero) serão multiplicados por 2. Abaixo temos a nossa lista de valores:

valores = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Assim como no exemplo anterior, podemos resolver utilizando um algoritmo básico.

# Lista que recebera o nosso resultado
valores_dobro = []

for valor in valores:
    if valor % 2 == 0:
        valores_dobro.append(valor * 2)

print(valores_dobro)

>>>
[4, 8, 12, 16, 20]

Podemos também resolver o mesmo problema utilizando as funções nativas map e filter:

valores_dobro = map(lambda valor: valor * 2, filter(lambda valor: valor % 2 == 0, valores))

Muito mais complicada não é? Apesar de resolver nosso problema, expressões como a acima são difíceis de ler e até mesmo de escrever. Em casos como esse, podemos novamente compactar nosso algoritmo utilizando a abrangência de lista.

valores_dobro = [valor * 2 for valor in valores if valor % 2 == 0]

Muito mais simples, não? Vamos para o próximo exemplo.

Exemplo 3

De maneira semelhante a lista, nós também podemos aplicar a abrangência em lista e dicionários. Segue um exemplo onde temos o seguinte dicionário:

 dicionario = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}

Vamos criar um segundo dicionário contendo apenas as chaves que são consoantes, ou seja, b, c, d e f, sendo que o valor para cada uma dessas chaves deve ser o dobro do valor armazenado na respectiva chave do dicionário original. Complicado? Em outras palavras, o novo dicionário deve ficar assim:

 novo_dicionario = {'b': 4, 'c': 6, 'd': 8, 'f': 12}

Utilizando um algoritmo genérico, podemos resolver o problema da seguinte maneira:

novo_dicionario = {}

for chave, valor in dicionario:
    if chave in ['b', 'c', 'd', 'f']:
        novo_dicionario[chave] = 2 * valor

print(novo_dicionario)

>>
{'b': 4, 'c': 6, 'd': 8, 'f': 12}

Aplicando agora a abrangência, conseguimos compactar o código acima de maneira interessante:

novo_dicionario = {chave: 2 * valor for chave, valor in dicionario.items() if chave in ['b', 'c', 'd', 'f']}

Conclusão

Chegamos ao final de mais um tutorial! Sempre temos de ter em mente que tão importante quanto escrever um código que funciona, é mantê-lo (seja por você ou por outro programador). Neste ponto, a abrangência de lista (e outras estruturas de dados) nos ajudam a escrever um código claro e fácil de dar manutenção.

Até o próximo tutorial pessoal!

Publicado originalmente: Abrangencia de listas e dicionários com Python

Referências

por Michell Stuttgart em 16 de January de 2017 às 12:37

January 15, 2017

Bruno Cezar Rocha

Migrando e-commerce do Iluria para o Shopify (usando Python)

Iluria

Ilúria é uma empresa brasileira de e-commerce que fornece uma plataforma bastante interessante para quem está começando uma loja virtual e precisa de algo simples e funcional. O sistema do Ilúria é realmente simples e de fácil uso, por isso continuo recomendando essa plataforma caso a sua necessidade seja básica e seu negócio ainda estiver começando.

Porém quando você começar a ter necessidades mais específicas, personalizações no sistema de sua loja e melhor colocação nas buscas o Ilúria infelizmente deixará de te atender e surgirá a necessidade de migrar para uma plataforma mais completa.

Vantagens do Iluria

  • É uma plataforma brasileira!
    • E isso ajuda a obter suporte.
    • É legal colaborar com o crescimento de empresas nacionais!
  • O preço é bom!
    • Eles fornecem 15 dias grátis
    • Para um catálogo de 50 produtos custa R$ 9,90, 200 produtos R$ 29,90 e o preço vai aumentando de acordo com a quantidade produtos cadastrados.
  • É fácil de usar
    • A plataforma tem uma admin bastante simples e fácil de usar.

Desvantagens do Iluria

  • GOOGLE & SEO
    • Apesar de o site dizer que é otimizado para o Google, o Iluria não fornece muitas opções de fácil acesso para otimizar o SEO da loja, não tem area para customizar URLs, descriptions, tags, sitemap etc... e isso seria possível apenas programando o template.
    • As configurações padrão de SEO não são suficientes para uma boa colocação no Google.
  • Falta de relatórios analíticos
    O Iluria não oferece muitos relatórios analíticos e isso torna bastante difícil fazer re-marketing.
    • Carrinho abandonado
      O Iluria não tem controle de carrinho de compras abandonado e isso impossibilita que você lembre seu cliente sobre compras esquecidas, e está é uma das melhoras práticas para recuperar vendas.
    • Buscas
      Outro relatório interessante seria o relatório de buscas, o cliente entra na sua loja e digita na busca "azul" mas não encontra as camisetas azuis que você tem no seu catálogo e então vai para outra loja. O iluria deveria ter um relatório para te informar isso, pois dessa forma você pode melhorar a descrição e tags de seus produtos que da próxima vez a "camiseta azul" seja encontrada!
  • Não tem API
    Essa é gravíssima, injustificável, imperdoável !!!!
    Estamos em 2017 e o mundo da web gira em torno de API qualquer possibilidade de estender, criar plugins, melhorar seria através de APIs, eu enviei um e-mail para o Iluria perguntando e disseram que simplesmente não tem nenhum tipo de API e isso é injustificável para uma empresa já estabelecida como a Iluria. Sem API os problemas são:
    • Não é possível estender as funcionalidades da plataforma.
    • Não é possível programar web-hooks para disparar envios de e-mails por exemplo usando IFTTT, Zapier e outras tecnologias de automação.
    • Não é possível cadastrar produtos em massa que você já tenha em seu banco de dados ou planilha.
    • Não é possível integrar com market places como MercadoLivre e Buscapé.
    • Não ajuda nem na hora de migrar para outra plataforma como vocês verão nos códigos a seguir.
  • Seus dados não te pertencem
    Bom, pelo menos é o que parece, e isso me leva a creditar que a falta de uma API possa fazer parte de alguma estratégia do Iluria para não permitir a saída dos clientes.
    • Exportar lista de clientes e produtos é limitada. No admin até existem as opções exportar para a lista de produtos e clientes, mas as informações exportadas não são suficientes. (mostrarei em seguida)

[IMG API]

Eu ainda continuo recomendando o Ilúria para quem está começando no e-commerce é com certeza a plataforma mais acessível. Mas realmente gostaria que essa empresa abrisse os olhos para a oportunidade que eles tem em mãos e trabalhassem para oferecer mais funcionalidades para os clientes, evitando assim que abandonem a plataforma quando começarem a crescer e eu ficaria mais feliz em estar aqui falando apenas das vantagens de uma plataforma nacional de e-commerce.

Preciso migrar e agora?

Na hora que precisar migrar para outra plataforma você precisa ter certeza de que o seu histórico não será perdido, e no caso do Iluria isso é em difícil pois as opções existentes não fornecem muitos dados.

O que você precisa manter:

  • Cadastro de produtos e variantes (contendo imagens)
  • Histórico de vendas
  • Cadastro de clientes

O primeiro desafio é conseguir esses dados, no caso do projeto em que eu trabalhei na migração decidimos não migrar nem o cadastro de clientes (pois já existia um cadastro em paralelo no MailChimp) e nem o histórico de vendas (pois é possível ter esta informação no gateway de pagamento).

Portanto o que posso mostrar aqui neste post é como migrar a sua lista de produtos cadastrados e para isso utilizei a lista limitada fornecida pelo Iluria como ponto de partida e também um script em Python para pegar os dados dos produtos via crawler.

Para exportar a lista de produtos utilize o menu relatórios -> estoque de produtos conforma a imagem abaixo:

[IMG EXPORT PROD]

Você irá baixar um arquivo .csv com o seguinte formato:

Produto;Nome;Varia��o 1;Varia��o 2;Varia��o 3;Estoque;Pre�o;Pre�o de custo;Nome do fornecedor
3F553C;Madeira 147;1,40 x 1,40;;;Sob encomenda;160,00;;
3F553B;Diversos 115;1,40 x 1,40;;;Sob encomenda;160,00;;
3F553B;Diversos 115;1,40 x 2,00;;;Sob encomenda;220,00;;

Você deve estar se perguntando o porquê dos caracteres no exemplo acima?

Bom como se já não bastasse tudo o que relacionei acima o Iluria ainda surpreende com mais isso, OS DADOS ESTÃO em codificação ISO-8859-1 e mais uma vez me perguntei em que ano estamos? porquê não está em UTF-8??

E as minhas imagens?

Bom, agora que entra a parte divertida, como percebem o arquivo que exportamos acima não traz muita informação sobre o produto, não tem o texto de descrição e também não tem uma referência para a imagem do produto, portanto resolveremos isso com Python!!!

Python FTW

Puxando os dados do Iluria através de crawling

O código dessa parte é bem simples (pode melhorar) mas o que apresento aqui é o que funcionou para mim nesta migração:

primeiro vamos criar um arquivo chamado utils.py

# coding: utf-8
import csv
import shutil
import requests
from bs4 import BeautifulSoup


def get_image_and_description(produto, link):
    """Baixa a imagem do iluria e salva no diretório atual
    Pega o texto de descrição do produto e retorna
    se não encontrar retorna None.
    """

    user_agent = {'User-agent': 'Mozilla/5.0'}
    response = requests.get(link, headers = user_agent)
    if response.status_code != 200:
        return
    soup = BeautifulSoup(response.content, "html.parser")

    image_element = soup.find("img", {"id": "big-image"})
    if image_element:
        image_url = "http:{0}".format(image_element['src'])
        image_url = image_url.replace("450xN", "850xN")

        image_content = requests.get(image_url, stream=True)
        if image_content.status_code == 200:
            filename = "{0}.jpg".format(produto)
            with open(filename, "wb") as image_file:
                shutil.copyfileobj(image_content.raw, image_file)
            del image_content

    description_element = soup.find(
        "div", {"class": "product-description"}, text=True)

    if description_element:
        return description_element[0]


# HACK ALLERT!!
# A função abaixo "imita" uma classe
def IluriaDictReader(data, **kwargs):
    """Lê o csv do Iluria em ISO-8859-1"""
    csv_reader = csv.DictReader(data, **kwargs)
    for row in csv_reader:
        yield {
          key: value.decode('iso-8859-1').encode('utf8')
          for key, value in row.iteritems()
        }

A primeira função acima irá fazer o download da imagem do Iluria e também salvar a descrição do produto já que essas informações não tem no csv exportado e caso você precise de outras informação basta adicionar mais elementos ao soup.find e retornar os dados.

Até aqui com as 2 funções acima já é possível imaginar em como fazer uma migração mas agora você tem que decidir para qual plataforma migrar, vamos falar delas!

Para onde ir?

Para este projeto analisamos algumas alternativas ao Iluria e tentamos dar preferência a alternativas nacionais e vamos falar delas.

  • Box Loja Essa pareceu ser uma opção bem próxima ao Iluria, os preços são bons entre 20 e 50 por mês de acordo com a quantidade de produtos, não cobram taxas por cada venda efetuada e parece ter algumas facilidades para customização. Porém mais uma vez o que tirou essa plataforma da lista de candidatos foi a falta e API, eu vasculhei o site deles, fóruns e Google, vi até em sites de freelancers pessoas procurando quem fizesse isso via algum script robô para migrar dados de Magento para o Box loja pelo fato deles ainda não terem API. E no rodapé do site deles não tem um link bem claro escrito developers ou API, e isso foi motivo suficiente para eliminar apesar de parecer uma boa opção.

  • Loja Integrada (recomendado !!!)
    Tá aqui mais uma opção que parece ser fantástica, e como nome sugere eles fornecem uma API!!! e por isso estão de parabéns!!! Esta plataforma é muito bem falada, e realmente parece uma plataforma de e-commerce que está a frente dos concorrentes, eu gostaria muito de ter migrado este projeto para o Loja Integrada, porém aqui pesou a questão comercial, os preços do Loja Integrada ainda não são tão convidativos quanto dos concorrentes acima, e isso parece ser justo pois oferecem mais vantagens, porém neste ponto aqui o Shopify acabou ganhando

Shopify

Shopify é uma empresa Canadense que oferece uma das mais utilizadas plataformas de e-commerce do mundo (supostamente 150 mil lojas) é escrita em Ruby on Rails, mas apesar disso possui uma API bastante completa e muito bem documentada. Além disso o Shopify criou o Liquid uma linguagem de template bastante fácil e inspirada no já conhecido Jinja portante muito confortável para qualquer programador Python interagir.

enter image description here

Vantagens do Shopify

  • Confiável
    • Uma empresa do porte do Shopify mantém um suporte bastante ativo e o minimo que podemos esperar é uma plataforma estável e confiável.
  • Integrada
    • A API do Shopify é muito bem escrita e com documentação completa tornando fácil interagir tanto para importar e exportar dados, quanto para criar aplicações que estendam as funcionalidades.
  • APPs
    • Por conta da API citada acima, o Shopify oferece um market place de APPs é possível encontra ruma variedade de plugins tando gratuitos quanto comerciais para instalar na sua loja e você também pode usar a API para criar seus próprios APPs.
  • Temas!!!
    • Isso é muito importante e o Shopify parece ter feito da forma certa, pois como já mencionei a linguagem de templates é fácil de usar então isto resulta em muitos templates disponíveis gratuitamente e também empresas como Envato, Themes Monster etc oferecendo várias opções comerciais, e isto também fácil para encontrar desenvolvedores para customizar temas.
  • SEO, Google, Buscas
    • Essa é uma parte muito interessante, de maneira simples é possível customizar as opções de SEO e os resultados são muito bons, poucas horas depois de migrar já tínhamos resultados diferentes no Google. (analisados através do Google developer tools)
  • Relatórios
    • O Shopify oferece desde o plano mais básico alguns relatórios essenciais como o controle de carrinhos abandonados, buscas efetuadas, balanço de vendas etc.. E os planos mais superiores ainda oferecem relatórios customizados.
  • Smart Collections
    • Aqui está outro recurso interessante, para quem tem muitos produtos e não quer ficar organizando manualmente é possível criar regras para que os produtos sejam automaticamente colocados em determinadas categorias/menus usando condições simples como sempre que aparecer 'camiseta' no título colocar este produto na categoria 'roupas', etc...
  • Preço
    • O plano básico do Shopify custa 30 dólares (pouco mais de 100,00) e oferece um grande número de funcionalidades e ainda é possível incluir APPs para obter ainda mais recursos.
  • Biliotecas
    • O próprio Shopify mantém bibliotecas em algumas linguagens como Ruby, Java, C#, Python e PHP para interagir com a API deles :)

Desvatagens do Shopify

  • Admin em Inglês
    • Os temas de front-end podem ser traduzidos para qualquer lingua, mas o admin apenas em inglês e isso dificulta bastante a adoção mesmo para quem fala a lingua inglesa pois alguns termos como "fulfillment" não são de fácil tradução.
  • Complexidade do Admin
    • Este problema só ocorre na primeira semana de uso, em pouco tempo você já se acostuma com a UI do admin, porém nas primeiras horas navegando você irá soltar muitos "What The Fuck???"
  • Compatibilidade com a realidade brasileira
    • O Shopify já atende bem tudo o que uma loja brasileira precisa, porém é bem claro que estão preparados para um estilo diferente de comércio, algumas coisas como taxas, estoque etc são feitas de um modo que não é usual no Brasil mas isso acredito que seja mais um problema de adaptação pois talvez nossos comércios que precisem se adaptar a um esquema mais organizado, e isso exige tempo e paciência.
  • Meios de pagamento
    • É possível configurar Paypal, PagSeguro, Mercadopago, Moip, Bitcoins entre outros. Eles tem o sistema preparado para trabalhar bem com todas essas plataformas, porém você só pode escolher Paypal + 1 para ser ativado em sua loja simultaneamente, ou seja, Paypal + Pagseguro, ou Paypal + Mercado Pago. Não é possível dar opção de seu cliente escolher qual gateway deseja usar.
  • Correios só através de app
    • Até existe um cálculo de correio integrado, mas é por faixa de CEP e você precisa configurar os preços manualmente, para ter um frete automático você precisa usar um APP adicional, é muito fácil de instalar, basta clicar em um botão! mas você tem que pagar + 5 dólares por mês para usar.

Mesmo com as desvantagens listadas acima o Shopify pareceu uma boa escolha, e a empresa está ativamente respondendo questões de brasileiros no seu fórum indicando que logo irão implementar mais facilidades e resolver essas limitações.

Decidimos migrar para o Shopify!!!

Interagindo com a API do Shopify via Python

Apesar do Shopify manter uma biblioteca Python para interagir com a API deles, eu analisei e achei que a solução mantida por eles não é muito Pythonica então continuei procurando.

Encontrei o projeto Python-Shopify que ainda não estava totalmente funcional e então fiz um fork e comecei a contribuir, arrumei alguns bugs e fiz o release para o PyPI. portanto agora é possível usar com pip install python-shopify.

Portanto em nosso projeto agora é a hora de criar o código que vai popular os produtos no banco de dados do Shopify usando aqueles dados que extraímos no código que mostrei acima.

Rode pip install python-shopify slugify tqdm e então no arquivo api.py

import os
from slugify import slugify
from shopify.products import (
    ProductsApiWrapper, Product, Image, Variant, Option
)


# FILL THE DATA below with data generated in Shopify-> admin -> apps
api_key = ''  
password = '' 
store_name = 'sua-loja'

paw = ProductsApiWrapper(api_key, password, store_name)

# Get a list of existing products, limited to 250 :(
existing = [item.title for item in paw.list(limit=250)]


def create_product(items):
    """Items is a list of dictionaries representing each product variant
    of the same product with the same ID and other data
    keys: ['description', 'price', 'name', 'link', 'size', 'stock']
    items = [
        # first variant holds full data and is default
        {'name': 'Awesome t-shirt',
         'code': '123456',
         'description': '<html>',
         'size': 'P',
         'price': '22.5',
         'stock': 2},
        # Other variants
        {'size': 'M',
         'price': '25.5',
         'stock': 2},
        {'size': 'G',
         'price': '29.5',
         'stock': 0},
    ]
    """

    # The first item should be the complete item holding all the fields
    # other items can have only the variants
    data = items[0]

    # Iluria gives us ISO-8859-1 :(
    name = data['name'].decode('utf-8')


    if name in existing or paw.list(title=name):
        # skip existing
        print "Already registered, skipping..."
        # or perform an update!!!
        return

    product = Product(
        title=data['name'],
        body_html=data['description'],
    )

    # There should be a 123456.jpg file in the same folder
    # alternatively you can use a URL provided in data
    image_filename = "{0}.jpg".format(data['code'])
    if os.path.exists(image_filename):
        image = Image()
        image.attach(image_filename)
        product.add_image(image)
    elif data.get('image_url'):
        product.add_image(Image(src=data['image_url']))

    # using the first word in title as tag
    # Product "T-shirt Blue 09" got tag "t-shirt"
    tag = data['name'].split()[0]
    tag = u''.join(i for i in tag if not i.isdigit())

    product.add_tags(tag.strip().lower())

    # You can add only 3 options
    # at positions 1, 2 and 3
    # you should add options before adding its variants
    product.add_option(
      Option(
        name="Size",
        position=1,
      )
    )

    for item in items:
        product.add_variant(
            Variant(
                option1=item['size'],
                # option2=data['size'],
                # option3=data['size'],
                title="Size",
                price=item['price'],
                # SKU should be different for each variant
                sku=data["code"] + slugify(item['size']), 
                position=1,
                inventory_policy="continue",
                fulfillment_service="manual",
                inventory_management="shopify",
                inventory_quantity=int(item['stock']),
                taxable=False,
                weight=300,
                weight_unit="g", # g, kg
                requires_shipping=True
            )
        )

    try:
        product = paw.create(product)
    except Exception as e:
        # do a proper logging here please!!!
        print e
        print product
        print items

    return product

Eu inclui o exemplo acima no repositório do Python-Shopify

Migrando do Iluria para o Shopify!!!

Agora precisamos juntar nossos 2 arquivos utils.py e api.py em um script e ai rodar a migração dos produtos.

import os
from collections import defaultdict
from tqdm import tqdm

from api import create_product
from utils import get_image_and_description, IluriaDictReader

BASE_URL = "http://www.sua_loja_iluria.com.br/pd-"
reader = IluriaDictReader(open('iluria_produtos_estoque.csv'), delimiter=";")
produtos = defaultdict(list)

for item in reader:
    size = item['Varia\xe7\xe3o 1']
    produtos[item["Produto"]].append(
       {
        "link": "{base}{slug}.html".format(
            base=BASE_URL, slug=item['Produto']
        ),
        "name": item["Nome"],
        "size": size,
        "price": item['Pre\xe7o'].replace(",", "."),
        "stock": item['Estoque'],
        "code": item['Produto']
       }
    )

for produto, items in tqdm(produtos.items()):
    data = items[0]  # cada item é uma lista com variações

    # pegamos a descrição e já fazemos o download da imagem
    # idealmente teriamos 2 funções, mas estamos só hackeando!!! :)
    data['description'] = get_image_and_description(
        data['code'], data['link']
    )

    if not os.path.exists('{0}.jpg'.format(produto)):
        # sem imagem sem cadastro!!!
        continue

    if not data['name']:
        # name é obrigatório
        continue

    # criamos o produto na API do Shopify e success!!!
    create_product(items)

Após o término do script você terá seus produtos cadastrados no Shopify e então os próximos passos será escolher e customizar seu tema (ou criar um próprio) e ai configurar sua loja, dominios, frete etc..

Apps recomendados:

Conclusão

Iluria uma empresa que tem uma boa plataforma mas que está perdendo a oportunidade de se tornar a maior plataforma de e-commerce brasileira por simplesmente não investir em evolução tecnológica de sua plataforma.

Loja Integrada aparentemente a melhor opção para quem quer se manter em uma plataforma brasileira, não tenho mais informações pois não cheguei realmente a utilizar, mas eles poderiam melhorar os preços de entrada, assim iriam atrair as lojas que inevitavelmente irão sair do Iluria

Shopify uma ótima opção, com muita coisa a melhorar para o público brasileiro mas mesmo assim com um pouco de dedicação e leitura das documentações é possível criar uma loja 100% funcional em poucas horas!!!

Para referencia: A loja que migramos no projeto citado é a https://fundosemtecido.com.br/ que comercializa fundos fotográficos para fotógrafos e conseguimos efetuar a migração desde a exportação dos dados, criação de tema personalizado, configurações de admin até colocar no ar em apenas 2 dias.

Links:

Python-Shopify

por Bruno Rocha em 15 de January de 2017 às 21:15

Humberto Rocha

Desbravando o pygame 1 - Conhecendo a Biblioteca

Desenvolver jogos é um lugar comum dentre muitas das pessoas que começam a estudar programação. Comigo não foi diferente, mesmo não seguindo a carreira de desenvolvimento de jogos esta sempre foi uma área que me chamou a atenção.

Estou criando esta série para aprender mais sobre desenvolvimento de jogos utilizando como ferramenta o pygame e para compartilhar minhas descobertas no processo. Irei desbrava-lo partindo de seus princípios básicos até a criação de um pequeno jogo pong single player.

Para isso, usarei programação procedural com o objetivo de atingir com mais facilidade os iniciantes em programação, finalizando com a refatoração para introduzir os conceitos de orientação a objetos para quem ainda não está familiarizado. Esta série foi elaborada tendo como base python 3.

Pygame

Pygame é um conjunto de módulos python para desenvolvimento de jogos. Ele adiciona funcionalidades em cima do SDL que é uma biblioteca multiplataforma que abstrai os componentes multimídia do computador como áudio e vídeo, permitindo o desenvolvimento com maior facilidade de programas que lidam com estes recursos como os jogos.

Instalação

Antes de prosseguir com a instalação você precisa do python instalado no sistema, caso não saiba como proceder veja os guias de instalação para linux, mac e windows.

O processo de instalação do pygame varia de acordo com o sistema operacional utilizado, no site oficial existem algumas opções de instalação descritas.

Para instalar em sistemas linux baseados em debian você precisa instalar antes os pacotes externos ao python através de:

$ sudo apt-get build-dep python-pygame

Em seguida, para instalar o pygame rode o seguinte comando em sua virtualenv:

$ pip install pygame

Ou no próprio sistema:

$ sudo pip install pygame

Olá mundo

Como de costume, ao aprender algo novo em programação vamos escrever um programa mínimo para verificar se está tudo funcionando e pronto para dar continuidade com o aprendizado.

Para isso criaremos um arquivo hello.py com o seguinte conteúdo:

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

import time

import pygame

pygame.init()

screen = pygame.display.set_mode([640, 480])

pygame.display.set_caption('Olá mundo')

screen.fill([0, 0, 0])

pygame.display.flip()

time.sleep(5)

Para rodar basta executar:

$ python hello.py

O resultado será este:

hello

Ainda está longe de ser um jogo, mas já temos alguns conceitos básicos importantes a serem comentados.

As três primeiras linhas são comuns a maioria dos programas escritos em python. Na primeira, definimos o tipo de codificação de caracteres usado pelo interpretador python que no caso foi utf-8. Esta linha não é necessária em python 3, mas para que as pessoas que estejam testando em python 2 não tenham problemas com acentuação prefiro manter este costume. As outras duas são a importação da biblioteca padrão time e da pygame que é onde reside toda a mágica.

O comando pygame.init() inicializa todos os módulos que necessitam de inicialização dentro do pygame.

Em pygame.display.set_mode criamos uma superfície para a nossa janela do tamanho 640x480 seguido de pygame.display.set_caption onde atribuímos o texto "Olá mundo" para o título da janela.

Com a janela criada, utilizamos o comando fill da janela screen para preenche-la com a cor preta. A cor é passada em formato de lista de três elementos que consistem dos valores do RGB que variam de 0 a 255. Troque os valores desta lista e rode novamente o programa para ver que a cor da janela mudar.

O comando pygame.display.flip representa um conceito importante do pygame e do desenvolvimento de jogos em geral. Quando usamos comandos para desenhar na tela, na verdade estamos desenhando na superfície criada em de memória que representa uma porção da nossa tela. Para copiar o conteúdo dela e mostra-lo na tela é preciso utilizar o flip que é análogo a você fazer o desenho para uma pessoa e ao terminar virar (flip) a superfície em que você estava desenhando para que ela veja. O flip atualiza a tela com o conteúdo desenhado na superfície screen.

Para finalizar o comando sleep da biblioteca time faz o programa esperar 5 segundos antes de finalizar. Caso contrário o programa fecharia antes que você possa ver o resultado.

Concluindo

Com isso, chegamos ao final deste primeiro capítulo apresentando a biblioteca e escrevendo nosso primeiro programa. O código deste capítulo encontra-se disponível neste repositório: desbravando-pygame.

Fique de olho nos próximos capítulos onde iremos descrever conceitos do desenvolvimento de jogos e sua aplicação dentro do pygame.

por Humberto Rocha em 15 de January de 2017 às 00:00

December 28, 2016

Gabbleblotchits

Minhashing all the things (part 1): microbial genomes

With the MinHash craze currently going on in the lab, we started discussing how to calculate signatures efficiently, how to index them for search and also how to distribute them. As a proof of concept I started implementing a system to read public data available on the Sequence Read Archive, as well as a variation of the Sequence Bloom Tree using Minhashes as leaves/datasets instead of the whole k-mer set (as Bloom Filters).

Since this is a PoC, I also wanted to explore some solutions that allow maintaining the least amount of explicit servers: I'm OK with offloading a queue system to Amazon SQS instead of maintaining a server running RabbitMQ, for example. Even with all the DevOps movement you still can't ignore the Ops part, and if you have a team to run your infrastructure, good for you! But I'm a grad student and the last thing I want to be doing is babysitting servers =]

Going serverless: AWS Lambda

The first plan was to use AWS Lambda to calculate signatures. Lambda is a service that exposes functions, and it manages all the runtime details (server provisioning and so on), while charging by the time and memory it takes to run the function. Despite all the promises, it is a bit annoying to balance everything to make an useful Lambda, so I used the Gordon framework to structure it. I was pretty happy with it, until I added our MinHash package and, since it is a C++ extension, needed to compile and send the resulting package to Lambda. I was using my local machine for that, but Lambda packaging is pretty much 'put all the Python files in one directory, compress and upload it to S3', which of course didn't work because I don't have the same library versions that Amazon Linux runs. I managed to hack a fix, but it would be wonderful if Amazon adopted wheels and stayed more in line with the Python Package Authority solutions (and hey, binary wheels even work on Linux now!).

Anyway, after I deployed the Lambda function and tried to run it... I fairly quickly realized that 5 minutes is far too short to calculate a signature. This is not a CPU-bound problem, it's just that we are downloading the data and network I/O is the bottleneck. I think Lambda will still be a good solution together with API Gateway for triggering calculations and providing other useful services despite the drawbacks, but at this point I started looking for alternative architectures.

Back to the comfort zone: Snakemake

Focusing on computing signatures first and thinking about other issues later, I wrote a quick Snakemake rules file and started calculating signatures for all the transcriptomic datasets I could find on the SRA. Totaling 671 TB, it was way over my storage capacity, but since both the SRA Toolkit and sourmash have streaming modes, I piped the output of the first as the input for the second and... voila! We have a duct-taped but working system. Again, the issue becomes network bottlenecks: the SRA seems to limit each IP to ~100 Mbps, it would take 621 days to calculate everything. Classes were happening during these development, so I just considered it good enough and started running it in a 32-core server hosted at Rackspace to at least have some signatures to play with.

Offloading computation: Celery + Amazon SQS

With classes over, we changed directions a bit: instead of going through the transcriptomic dataset, we decided to focus on microbial genomes, especially all those unassembled ones on SRA. (We didn't forget the transcriptomic dataset, but microbial genomes are small-ish, more manageable and we already have the microbial SBTs to search against). There are 412k SRA IDs matching the new search, totalling 28 TB of data. We have storage to save it, but since we want a scalable solution (something that would work with the 8 PB of data in the SRA, for example), I avoided downloading all the data beforehand and kept doing it in a streaming way.

I started to redesign the Snakemake solution: first thing was to move the body of the rule to a Celery task and use Snakemake to control what tasks to run and get the results, but send the computation to a (local or remote) Celery worker. I checked other work queue solutions, but they were either too simple or required running specialized servers. (and thanks to Gabriel Marcondes for enlightening me about how to best use Celery!). With Celery I managed to use Amazon SQS as a broker (the queue of tasks to be executed, in Celery parlance), and celery-s3 as the results backend. While not an official part of Celery, using S3 to keep results allowed to avoid deploying another service (usually Celery uses redis or RabbitMQ for result backend). I didn't configure it properly tho, and ended up racking up \$200 in charges because I was querying S3 too much, but my advisor thought it was funny and mocked me on Twitter (I don't mind, he is the one paying the bill =P). For initial tests I just ran the workers locally on the 32-core server, but... What if the worker was easy to deploy, and other people wanted to run additional workers?

Docker workers

I wrote a Dockerfile with all the dependencies, and made it available on Docker hub. I still need to provide credentials to access SQS and S3, but now I can deploy workers anywhere, even... on the Google Cloud Platform. They have a free trial with \$300 in credits, so I used the Container Engine to deploy a Kubernetes cluster and run workers under a Replication Controller.

Just to keep track: we are posting Celery tasks from a Rackspace server to Amazon SQS, running workers inside Docker managed by Kubernetes on GCP, putting results on Amazon S3 and finally reading the results on Rackspace and then posting it to IPFS. IPFS is the Interplanetary File System, a decentralized solution to share data. But more about this later!

HPCC workers

Even with Docker workers running on GCP and the Rackspace server, it was progressing slowly and, while it wouldn't be terribly expensive to spin up more nodes on GCP, I decided to go use the resources we already have: the MSU HPCC. I couldn't run Docker containers there (HPC is wary of Docker, but we are trying to change that!), so I used Conda to create a clean environment and used the requirements file (coupled with some PATH magic) to replicate what I have inside the Docker container. The Dockerfile was very useful, because I mostly ran the same commands to recreate the environment. Finally, I wrote a submission script to start a job array with 40 jobs, and after a bit of tuning I decided to use 12 Celery workers for each job, totalling 480 workers.

This solution still requires a bit of babysitting, especially when I was tuning how many workers to run per job, but it achieved around 1600 signatures per hour, leading to about 10 days to calculate for all 412k datasets. Instead of downloading the whole dataset, we are reading the first million reads and using our streaming error trimming solution to calculate the signatures (and also to test if it is the best solution for this case).

Clever algorithms are better than brute force?

While things were progressing, Titus was using the Sequence Bloom Tree + Minhash code to categorize the new datasets into the 50k genomes in the [RefSeq] database, but 99\% of the signatures didn't match anything. After assembling a dataset that didn't match, he found out it did match something, so... The current approach is not so good.

(UPDATE: it was a bug in the search, so this way of calculating signatures probably also work. Anyway, the next approach is faster and more reasonable, so yay bug!)

Yesterday he came up with a new way to filter solid k-mers instead of doing error trimming (and named it... syrah? Oh, SyRAh... So many puns in this lab). I created a new Celery task and refactored the Snakemake rule, and started running it again... And wow is it faster! It is currently doing around 4200 signatures per hour, and it will end in less than five days. The syrah approach probably works for the vast majority of the SRA, but metagenomes and metatranscriptomes will probably fail because the minority members of the population will not be represented. But hey, we have people in the lab working on that too =]

Future

The solution works, but several improvements can be made. First, I use Snakemake at both ends, both to keep track of the work done and get the workers results. I can make the workers a bit smarter and post the results to a S3 bucket, and so I only need to use Snakemake to track what work needs to be done and post tasks to the queue. This removes the need for celery-s3 and querying S3 all the time, and opens the path to use Lambda again to trigger updates to IPFS.

I'm insisting on using IPFS to make the data available because... Well, it is super cool! I always wanted to have a system like bittorrent to distribute data, but IPFS builds up on top of other very good ideas from bitcoin (bitswap), and git (the DAG representation) to make a resilient system and, even more important, something that can be used in a scientific context to both increase bandwidth for important resources (like, well, the SRA) and to make sure data can stay around if the centralized solution goes away. The Cancer Gene Trust project is already using it, and I do hope more projects show up and adopt IPFS as a first-class dependency. And, even crazier, we can actually use IPFS to store our SBT implementation, but more about this in part 2!

por luizirber em 28 de December de 2016 às 14:00

December 27, 2016

JungleCoders

Consultas via Telegram

Seria legal se profissionais de informática dessem consultas como médicos ou advogados, mas algo nos impede de cobrar por tudo e esse desejo ou intenção de compartilhar ideias nos consome.

Eu participo de vários grupos de Telegram, principalmente sobre Python, um deles é o PyCoding e o outro é o pybr. Normalmente eu leio os grupos quando estou usando meu celular, então nem sempre é possível ajudar com as dúvidas, mas vou tentar separar um pouco de tempo para explorar algumas ideias aqui e lá.

Hoje está tão fácil aprender qualquer coisa que tenho notado uma ansiedade cada vez maior de quem começa a programar de aprender tudo. Em um só mês, algumas pessoas querem aprender Python, SciPi, TensorFlow, Android e o que mais der. Um mês é pouco tempo. Pode-se aprender a programar em períodos relativamente pequenos, mas leva tempo para se acostumar com as novas ideias, linguagens e bibliotecas. O Peter Norvig comentou sobre essa ansiedade no Learn Programming in Ten Years.

Cálculo de médias


Vamos ao interesse do post, a tal consulta.

O colega Wesley enviou dois programas, vou começar pelo mais simples. Primeiro vamos desconsiderar os palavrões, nosso colega é jovem.

Uma coisa que gostei muito foi a primeira linha de mensagens. Poucos se preocupam em dizer o que faz o programa, isso é legal! Eu faria apenas uma pequena modificação para que a linha não fosse tão grande.

Como eu cresci nos anos 80, sem letras minúsculas e acentos, é questão de honra corrigir as mensagens.

Nas linhas 7 a 11, os valores das variávies m1, n1, n2, n3 e n4 são solicitados. Como a função input retorna strings, veja que no resto do programa a função float foi utilizada para converter estes valores. Neste caso, o valor convertido deveria ser armazenado diretamente na variável.

Desta forma, simplificamos a linha 12 de forma a facilmente perceber um erro de prioridade de operações. Quando fazemos o cálculo de n1 + n2 + n3 + n4 / 4, sem utilizar parênteses, as operações são realizados por ordem de prioridade, como na matemática. Assim, n4/4 é somado a n1, n2 e n3. Para calcular a média, precisamos de parênteses: (n1 + n2 + n3 + n4) / 4. Agora, a soma das notas é calculada e depois dividida por quatro, como queríamos.

Entre as linhas 15 e 19 acredito que tenha sido apenas um teste. Vou remover para não atrapalhar o entendimento do programa final.

As linhas de 24 a 35 imprimem vários pontos, vou apenas simplificar.

Para terminar, pequenas modificações para usar as f strings do Python 3.6.


Programa com tkinter

O outro programa é uma interface gráfica, usando tkinter.

Como os fontes foram postados no Telegram, muito se perde. De cara há um problema com o import da linha 2. Eu parabenizo o Wesley pela coragem de usar o tkinter. É umas das partes do Python que menos gosto, mas que funciona.


Deve-se evitar os import * no Python, isso polui o namespace e causa problemas chatos de resolver. No caso do tkinter, é um caso a se pensar, mas nunca misturar o * com os imports de classes e funções individuais.

Uma coisa que salta aos olhos, não, não falo do fundo caladryl, mas da repetição da cor em várias partes do código. Vamos criar uma constante para cor de fundo, melhor, vamos retirar as cores e deixar as cores padrão.

Um outro problema é a validação de valores, acrescentei uma função float_ou_zero que retorna zero caso o valor digitado não possa ser convertido para float.

Usar tkinter sem classes é um tanto confuso, eu particularmente não gosto de ter funções com variáveis globais e de ter definições de funções e variáveis misturadas, mas isso é assunto para outro post.


Vejamos como ficou!




Convertendo ints

Outro post interessante foi o de como converter vários ints de uma só vez. O problema inicial era calcular um valor do tipo hh:mm:ss em total de segundos.


O que me chamou atenção foi uma das soluções:

Correta, porém, achei que o foco da solução não era mais o problema inicial, mas fazer em menos linhas. De repente, passa o medo de "Perlizar" o Python.
O problema em si, exige validação dos dados. Este é um detalhe importante que é fácil de ser esquecido. Então, ao invés de fazer com menos linhas, vamos adicionar o mínimo de validação.
Esta solução utiliza o módulo datetime do Python e o tipo time para validar as horas entre 0 e 23, minutos entre 0 e 60 e o mesmo para segundos. Se o usuário entrar um valor errado, terá que redigitar após receber uma mensagem de erro. Embora eu tenha usado a expansão de listas duas vezes em uma só linha (Perlização?), acho que o código ficou relativamente bom.

São detalhes, mas que fazem a diferença em programas maiores. Nem sempre escrever em menos linhas é o mais correto ou deveria ser o foco principal da solução de um problema.

Ainda sobra margem para uma outra solução, onde criamos uma função para converter horas, minutos e segundos para total em segundos.

Além da validação (ainda que mínima), ganhamos a flexibilidade de digitar valores como 10, 10:20 ou 10:20:30. O programa que fizemos pode ser importado por outros programas e suas funções reutilizadas, sem perder a funcionalidade inicial se usado como programa principal.

por Nilo Menezes (noreply@blogger.com) em 27 de December de 2016 às 16:24

December 12, 2016

Aprenda Python

TDD serve como documentação

No artigo [Por que TDD?](/2016/10/por-que-tdd.html) eu escrevi sobre alguns benefícios de usar TDD, mas foi um texto bem conceitual, sem exemplos. Nesse aqui eu quero mostrar um dos benefícios de testes automatizados na prática: servir como documentação técnica funcional. Leia com atenção o trecho de código abaixo: um_mes_atras = datetime.date.today() - timedelta(days=30) URL = (

por Vinicius Assef (noreply@blogger.com) em 12 de December de 2016 às 12:38

December 10, 2016

Thiago Avelino

SENAI abre espaço para compartilhamento da minha história de empreendimento na área Adulto (+18)

O sonho do dono de Startup é ter uma empresa comprada, o que você realmente pensa em fazer com esse dinheiro?

Para quem caiu aqui sem saber quem é o Avelino, sou ex-dono do sambaporno.com (agregador de conteúdo Adulto, a inteligência é categorização) onde tínhamos média 55 milhões de pageviews/dia, para entender da evolução tecnológica do Samba veja o vídeo abaixo.

https://medium.com/media/414bd0d1a1cf12ec8a9db16bb774f427/href

Hoje (dia 10/12/2016) tive o prazer de bater um papo com os alunos (futuro empreendedores) do SENAI, foi uma palestra onde eu contei minha história no mundo de empreendedorismo indo da concepção do projeto até venda para uma grade empresa de fora do Brasil, abrindo algumas tomadas de decisão onde precisava fazer o site dar dinheiro para pagar os gastos. O Samba nasceu como um site pessoal e isso me gerou alguns problemas, pois não encarava como um negócio e sim como um brinquedo pessoal (a bola é minha, se eu jogar ninguém joga).

Devemos variar nossos investimento, Filmow entrou na minha carteira de investimento por esse motivo

Depois de anos fazendo o Samba acontece eu resolvi abrir meu leque de negócios e entrei de sócio no Filmow (rede social para cinéfilo), foi uma ótima experiência em lidar com usuários conectado onde a página precisa ser customizada para cada cliente, estamos em processo de atualização do sistema atual (escrito em Python e banco de dados PostgreSQL) para usar menos recurso de servidor, usando processamento concorrente.

Falei como é o meu processo de selecionar a tecnologia que vou usar para fazer determinado projeto, se meu projeto (startup) está nascendo agora eu vejo o que é mais rápido para colocar no seu, sem pensa em escalabilidade e/ou qual quer tipo de tecnologia muito avançada. Empreender é ter risco então precisamos minimizar ao máximo nosso risco.

Para finalizar falei um pouco para usarmos projetos open source para acelerar o processo de colocar o MVP em produção, mas precisamos sempre está atento com a licença do software que estamos usando, se esse software X tem licença restritiva em colocar esse código comercialmente isso pode ser um problema na hora de colocar cliente usando seu software.

Após a palestra, gravamos um vídeo para página do Facebook do SENAI onde falei aonde vejo que devemos focar esforços nos próximos anos (o que vejo do futuro):

https://medium.com/media/b27b2832b515b62a07b5553a59b689e0/href

por Avelino em 10 de December de 2016 às 19:36