skip to navigation
skip to content

Planet Python

Last update: April 26, 2017 10:47 AM

April 26, 2017


PythonClub - A Brazilian collaborative blog about Python

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:

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:

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

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 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!"

April 26, 2017 10:41 AM


Gocept Weblog

Last call for take off to the Python 3 wonderland

We are approaching the Zope 2 Resurrection Sprint and hope that all those who are willing to help earl Zope II on his endeavor to port his realm have already prepared there horses and packed the necessary equipment to arrive in Halle (Saale), Germany.

To help with the preparations we have set up some means of communication:

Etherpad

In the Etherpad we have collected a fundamental set of obstacles that the immigration authority of Python 3 wonderland send to earl Zope II via a mounted messenger. If there are additional problems we need to solve with the immigration or other authorities, feel free to point those out in the pad.

IRC Channel

During the sprint we will have an owl waiting for messages in the #sprint channel on irc.freenode.net, so additional information and questions can be placed there.

General Schedule

In general the gates of the gocept manor in Halle (Saale) are open from 8:00 till 18:00 during the sprint for the squires to help earl Zope II. There will be some refreshments in the morning (8:00 – 9:00) and during lunch time (12:00 – 13:00) in order to keep everyone happy and content.

Apart from that, there will be some fixed points in time to meet:

We are looking forward to the sprint and hope to overcome the remaining migration problems of earl Zope II.


April 26, 2017 06:56 AM

April 25, 2017


Carl Chenet

Migrate Feed2tweet from 1.0 to 1.1

Feed2tweet 1.1, your RSS to Twitter bot, had a compatibility-breaking change: the format of the cache file changed because of a Python platform-dependent issue of one of the dependencies.

 

It is mandatory to execute the following steps in order to keep safe the timeline of your Twitter account (and maybe more important the timelines of your followers).

How to migrate Feed2tweet 1.0 to 1.1

We start by commenting the Feed2tweet entry in the system crontab:

# */10 * * * * feed2tweet feed2tweet -c /etc/feed2tweet/feed2tweet.ini

The next step is to update Feed2tweet:

# pip3 install feed2tweet --upgrade

As we may have an issue during the upgrade, we back up the cache file:

$ mkdir feed2tweet-backup
$ cp /var/lib/feed2tweet/feed2tweet.db feed2tweet-backup/

Now we remove the Feed2tweet cache file:

$ rm -f /var/lib/feed2tweet/feed2tweet.db

So far so good, let’s regenerate the cache file by getting all then entries but NOT sending them to Twitter:

$ feed2tweet --populate-cache -c /etc/feed2tweet/feed2tweet.ini

Almost finished. We execute the dry run mode in order to check the result. You should not have any displayed entry, meaning you’re now ready to send the next ones to Twitter:

$ feed2tweet --dry-run -c /etc/feed2tweet/feed2tweet.ini

And of course to finish we uncomment the Feed2tweet line in the /etc/crontab file

*/10 * * * * feed2tweet feed2tweet -c /etc/feed2tweet/feed2tweet.ini

We’re all set! The new RSS entries of your feeds will be automatically posted to Twitter.

More information about Feed2tweet

… and finally

You can help the Feed2tweet Bot by donating anything through Liberaypay (also possible with cryptocurrencies). That’s a big factor motivation 😉

April 25, 2017 10:00 PM


Philip Semanchuk

Thanks, Science!

I took part in the Raleigh March for Science last Saturday. For the opportunity to learn about it, participate in it, photograph it, share it with you — oh, and also for, you know, being alive today — thanks, science!







April 25, 2017 09:20 PM


DataCamp

Keras Cheat Sheet: Neural Networks in Python

Keras is an easy-to-use and powerful library for Theano and TensorFlow that provides a high-level neural networks API to develop and evaluate deep learning models.

We recently launched one of the first online interactive deep learning course using Keras 2.0, called "Deep Learning in Python.

Now, DataCamp has created a Keras cheat sheet for those who have already taken the course and that still want a handy one-page reference or for those who need an extra push to get started.

In short, you'll see that this cheat sheet not only presents you with the six steps that you can go through to make neural networks in Python with the Keras library.

keras cheat sheet

In no time, this Keras cheat sheet will make you familiar with how you can load datasets from the library itself, preprocess the data, build up a model architecture, and compile, train, and evaluate it. As there is a considerable amount of freedom in how you build up your models, you'll see that the cheat sheet uses some of the simple key code examples of the Keras library that you need to know to get started with building your own neural networks in Python.

Furthermore, you'll also see some examples of how to inspect your model, and how you can save and reload it. Lastly, you’ll also find examples of how you can predict values for test data and how you can fine tune your models by adjusting the optimization parameters and early stopping.

Everything you need to make your first neural networks in Python with Keras!

Also, don't miss out on our scikit-learn cheat sheet, NumPy cheat sheet and Pandas cheat sheet!

April 25, 2017 06:10 PM


Mike Driscoll

Getting Started with pywebview

I stumbled across the pywebview project a couple of weeks ago. The pywebview package “is a lightweight cross-platform wrapper around a webview component that allows to display HTML content in its own native GUI window.” It uses WebKit on OSX and Linux and Trident (MSHTML) on Windows, which is actually what wxPython’s webview widget also does. The idea behind pywebview is that it provides you the ability to load a website in a desktop application, kind of Electron.

While pywebview claims it “has no dependencies on an external GUI framework”, on Windows it requires pythonnet, PyWin32 and comtypes installed. OSX requires “pyobjc”, although that is included with the default Python installed in OSX. For Linux, it’s a bit more complicated. On GTK3 based systems you will need PyGObject whereas on Debian based systems, you’ll need to install PyGObject + gir1.2-webkit-3.0. Finally, you can also use PyQt 4 or 5.

You can use Python micro-web frameworks, such as Flask or bottle, with pywebview to create cool applications using HTML5 instead of Python.

To install pywebview itself, just use pip:


pip install pywebview

Once installed and assuming you also have the prerequisites, you can do something like this:

import webview
 
webview.create_window('My Web App', 'http://www.mousevspython.com')

This will load the specified URL in a window with the specified title (i.e. the first argument). Your new application should end up looking something like this:

The API for pywebview is quite short and sweet and can be found here:

There are only a handful of methods that you can use, which makes them easy to remember. But since you can’t create any other controls for your pywebview application, you will need to do all your user interface logic in your web application.

The pywebview package supports being frozen using PyInstaller for Windows and py2app for OSX. It also works with virtualenv, although there are known issues that you will want to read about before using virtualenv.

Wrapping Up

The pywebview package is actually pretty neat and I personally think it’s worth a look. If you want something that’s a bit more integrated to your desktop, then you might want to give wxPython or PyQt a try. But if all you need to do is distribute an HTML5-based web app, then this package might be just the one for you.

April 25, 2017 12:30 PM


Chris Moffitt

Effectively Using Matplotlib

Introduction

The python visualization world can be a frustrating place for a new user. There are many different options and choosing the right one is a challenge. For example, even after 2 years, this article is one of the top posts that lead people to this site. In that article, I threw some shade at matplotlib and dismissed it during the analysis. However, after using tools such as pandas, scikit-learn, seaborn and the rest of the data science stack in python - I think I was a little premature in dismissing matplotlib. To be honest, I did not quite understand it and how to use it effectively in my workflow.

Now that I have taken the time to learn some of these tools and how to use them with matplotlib, I have started to see matplotlib as an indispensable tool. This post will show how I use matplotlib and provide some recommendations for users getting started or users who have not taken the time to learn matplotlib. I do firmly believe matplotlib is an essential part of the python data science stack and hope this article will help people understand how to use it for their own visualizations.

Why all the negativity towards matplotlib?

In my opinion, there are a couple of reasons why matplotlib is challenging for the new user to learn.

First, matplotlib has two interfaces. The first is based on MATLAB and uses a state-based interface. The second option is an an object-oriented interface. The why’s of this dual approach are outside the scope of this post but knowing that there are two approaches is vitally important when plotting with matplotlib.

The reason two interfaces cause confusion is that in the world of stack overflow and tons of information available via google searches, new users will stumble across multiple solutions to problems that look somewhat similar but are not the same. I can speak from experience. Looking back on some of my old code, I can tell that there is a mishmash of matplotlib code - which is confusing to me (even if I wrote it).

Key Point
New matplotlib users should learn and use the object oriented interface.

Another historic challenge with matplotlib is that some of the default style choices were rather unattractive. In a world where R could generate some really cool plots with ggplot, the matplotlib options tended to look a bit ugly in comparison. The good news is that matplotlib 2.0 has much nicer styling capabilities and ability to theme your visualizations with minimal effort.

The third challenge I see with matplotlib is that there is confusion as to when you should use pure matplotlib to plot something vs. a tool like pandas or seaborn that is built on top of matplotlib. Anytime there can be more than one way to do something, it is challenging for the new or infrequent user to follow the right path. Couple this confusion with the two different API’s and it is a recipe for frustration.

Why stick with matplotlib?

Despite some of these issues, I have come to appreciate matplotlib because it is extremely powerful. The library allows you to create almost any visualization you could imagine. Additionally, there is a rich ecosystem of python tools built around it and many of the more advanced visualization tools use matplotlib as the base library. If you do any work in the python data science stack, you will need to develop some basic familiarity with how to use matplotlib. That is the focus of the rest of this post - developing a basic approach for effectively using matplotlib.

Basic Premises

If you take nothing else away from this post, I recommend the following steps for learning how to use matplotlib:

  1. Learn the basic matplotlib terminology, specifically what is a Figure and an Axes .
  2. Always use the object-oriented interface. Get in the habit of using it from the start of your analysis.
  3. Start your visualizations with basic pandas plotting.
  4. Use seaborn for the more complex statistical visualizations.
  5. Use matplotlib to customize the pandas or seaborn visualization.

This graphic from the matplotlib faq is gold. Keep it handy to understand the different terminology of a plot.

Matplotlib parts

Most of the terms are straightforward but the main thing to remember is that the Figure is the final image that may contain 1 or more axes. The Axes represent an individual plot. Once you understand what these are and how to access them through the object oriented API, the rest of the process starts to fall into place.

The other benefit of this knowledge is that you have a starting point when you see things on the web. If you take the time to understand this point, the rest of the matplotlib API will start to make sense. Also, many of the advanced python packages like seaborn and ggplot rely on matplotlib so understanding the basics will make those more powerful frameworks much easier to learn.

Finally, I am not saying that you should avoid the other good options like ggplot (aka ggpy), bokeh, plotly or altair. I just think you’ll need a basic understanding of matplotlib + pandas + seaborn to start. Once you understand the basic visualization stack, you can explore the other options and make informed choices based on your needs.

Getting Started

The rest of this post will be a primer on how to do the basic visualization creation in pandas and customize the most common items using matplotlib. Once you understand the basic process, further customizations are relatively straightforward.

I have focused on the most common plotting tasks I encounter such as labeling axes, adjusting limits, updating plot titles, saving figures and adjusting legends. If you would like to follow along, the notebook includes additional detail that should be helpful.

To get started, I am going to setup my imports and read in some data:

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

df = pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/sample-salesv3.xlsx?raw=true")
df.head()
account number name sku quantity unit price ext price date
0 740150 Barton LLC B1-20000 39 86.69 3380.91 2014-01-01 07:21:51
1 714466 Trantow-Barrows S2-77896 -1 63.16 -63.16 2014-01-01 10:00:47
2 218895 Kulas Inc B1-69924 23 90.70 2086.10 2014-01-01 13:24:58
3 307599 Kassulke, Ondricka and Metz S1-65481 41 21.05 863.05 2014-01-01 15:05:22
4 412290 Jerde-Hilpert S2-34077 6 83.21 499.26 2014-01-01 23:26:55

The data consists of sales transactions for 2014. In order to make this post a little shorter, I’m going to summarize the data so we can see the total number of purchases and total sales for the top 10 customers. I am also going to rename columns for clarity during plots.

top_10 = (df.groupby('name')['ext price', 'quantity'].agg({'ext price': 'sum', 'quantity': 'count'})
          .sort_values(by='ext price', ascending=False))[:10].reset_index()
top_10.rename(columns={'name': 'Name', 'ext price': 'Sales', 'quantity': 'Purchases'}, inplace=True)

Here is what the data looks like.

Name Purchases Sales
0 Kulas Inc 94 137351.96
1 White-Trantow 86 135841.99
2 Trantow-Barrows 94 123381.38
3 Jerde-Hilpert 89 112591.43
4 Fritsch, Russel and Anderson 81 112214.71
5 Barton LLC 82 109438.50
6 Will LLC 74 104437.60
7 Koepp Ltd 82 103660.54
8 Frami, Hills and Schmidt 72 103569.59
9 Keeling LLC 74 100934.30

Now that the data is formatted in a simple table, let’s talk about plotting these results as a bar chart.

As I mentioned earlier, matplotlib has many different styles available for rendering plots. You can see which ones are available on your system using plt.style.available .

plt.style.available
['seaborn-dark',
 'seaborn-dark-palette',
 'fivethirtyeight',
 'seaborn-whitegrid',
 'seaborn-darkgrid',
 'seaborn',
 'bmh',
 'classic',
 'seaborn-colorblind',
 'seaborn-muted',
 'seaborn-white',
 'seaborn-talk',
 'grayscale',
 'dark_background',
 'seaborn-deep',
 'seaborn-bright',
 'ggplot',
 'seaborn-paper',
 'seaborn-notebook',
 'seaborn-poster',
 'seaborn-ticks',
 'seaborn-pastel']

Using a style is as simple as:

plt.style.use('ggplot')

I encourage you to play around with different styles and see which ones you like.

Now that we have a nicer style in place, the first step is to plot the data using the standard pandas plotting function:

top_10.plot(kind='barh', y="Sales", x="Name")
Pandas plot 1

The reason I recommend using pandas plotting first is that it is a quick and easy way to prototype your visualization. Since most people are probably already doing some level of data manipulation/analysis in pandas as a first step, go ahead and use the basic plots to get started.

Customizing the Plot

Assuming you are comfortable with the gist of this plot, the next step is to customize it. Some of the customizations (like adding titles and labels) are very simple to use with the pandas plot function. However, you will probably find yourself needing to move outside of that functionality at some point. That’s why I recommend getting in the habit of doing this:

fig, ax = plt.subplots()
top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)

The resulting plot looks exactly the same as the original but we added an additional call to plt.subplots() and passed the ax to the plotting function. Why should you do this? Remember when I said it is critical to get access to the axes and figures in matplotlib? That’s what we have accomplished here. Any future customization will be done via the ax or fig objects.

We have the benefit of a quick plot from pandas but access to all the power from matplotlib now. An example should show what we can do now. Also, by using this naming convention, it is fairly straightforward to adapt others’ solutions to your unique needs.

Suppose we want to tweak the x limits and change some axis labels? Now that we have the axes in the ax variable, we have a lot of control:

fig, ax = plt.subplots()
top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)
ax.set_xlim([-10000, 140000])
ax.set_xlabel('Total Revenue')
ax.set_ylabel('Customer');
Pandas plot 2

Here’s another shortcut we can use to change the title and both labels:

fig, ax = plt.subplots()
top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)
ax.set_xlim([-10000, 140000])
ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')
Pandas plot 3

To further demonstrate this approach, we can also adjust the size of this image. By using the plt.subplots() function, we can define the figsize in inches. We can also remove the legend using ax.legend().set_visible(False)

fig, ax = plt.subplots(figsize=(5, 6))
top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)
ax.set_xlim([-10000, 140000])
ax.set(title='2014 Revenue', xlabel='Total Revenue')
ax.legend().set_visible(False)
Pandas plot 4

There are plenty of things you probably want to do to clean up this plot. One of the biggest eye sores is the formatting of the Total Revenue numbers. Matplotlib can help us with this through the use of the FuncFormatter . This versatile function can apply a user defined function to a value and return a nicely formatted string to place on the axis.

Here is a currency formatting function to gracefully handle US dollars in the several hundred thousand dollar range:

def currency(x, pos):
    'The two args are the value and tick position'
    if x >= 1000000:
        return '${:1.1f}M'.format(x*1e-6)
    return '${:1.0f}K'.format(x*1e-3)

Now that we have a formatter function, we need to define it and apply it to the x axis. Here is the full code:

fig, ax = plt.subplots()
top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)
ax.set_xlim([-10000, 140000])
ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')
formatter = FuncFormatter(currency)
ax.xaxis.set_major_formatter(formatter)
ax.legend().set_visible(False)
Pandas plot 5

That’s much nicer and shows a good example of the flexibility to define your own solution to the problem.

The final customization feature I will go through is the ability to add annotations to the plot. In order to draw a vertical line, you can use ax.axvline() and to add custom text, you can use ax.text() .

For this example, we’ll draw a line showing an average and include labels showing three new customers. Here is the full code with comments to pull it all together.

# Create the figure and the axes
fig, ax = plt.subplots()

# Plot the data and get the averaged
top_10.plot(kind='barh', y="Sales", x="Name", ax=ax)
avg = top_10['Sales'].mean()

# Set limits and labels
ax.set_xlim([-10000, 140000])
ax.set(title='2014 Revenue', xlabel='Total Revenue', ylabel='Customer')

# Add a line for the average
ax.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)

# Annotate the new customers
for cust in [3, 5, 8]:
    ax.text(115000, cust, "New Customer")

# Format the currency
formatter = FuncFormatter(currency)
ax.xaxis.set_major_formatter(formatter)

# Hide the legend
ax.legend().set_visible(False)
Pandas plot 6

While this may not be the most exciting plot it does show how much power you have when following this approach.

Figures and Plots

Up until now, all the changes we have made have been with the indivudual plot. Fortunately, we also have the ability to add multiple plots on a figure as well as save the entire figure using various options.

If we decided that we wanted to put two plots on the same figure, we should have a basic understanding of how to do it. First, create the figure, then the axes, then plot it all together. We can accomplish this using plt.subplots() :

fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(7, 4))

In this example, I’m using nrows and ncols to specify the size because this is very clear to the new user. In sample code you will frequently just see variables like 1,2. I think using the named parameters is a little easier to interpret later on when you’re looking at your code.

I am also using sharey=True so that the yaxis will share the same labels.

This example is also kind of nifty because the various axes get unpacked to ax0 and ax1 . Now that we have these axes, you can plot them like the examples above but put one plot on ax0 and the other on ax1 .

# Get the figure and the axes
fig, (ax0, ax1) = plt.subplots(nrows=1,ncols=2, sharey=True, figsize=(7, 4))
top_10.plot(kind='barh', y="Sales", x="Name", ax=ax0)
ax0.set_xlim([-10000, 140000])
ax0.set(title='Revenue', xlabel='Total Revenue', ylabel='Customers')

# Plot the average as a vertical line
avg = top_10['Sales'].mean()
ax0.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)

# Repeat for the unit plot
top_10.plot(kind='barh', y="Purchases", x="Name", ax=ax1)
avg = top_10['Purchases'].mean()
ax1.set(title='Units', xlabel='Total Units', ylabel='')
ax1.axvline(x=avg, color='b', label='Average', linestyle='--', linewidth=1)

# Title the figure
fig.suptitle('2014 Sales Analysis', fontsize=14, fontweight='bold');

# Hide the legends
ax1.legend().set_visible(False)
ax0.legend().set_visible(False)
Pandas plot 7

Up until now, I have been relying on the jupyter notebook to display the figures by virtue of the %matplotlib inline directive. However, there are going to be plenty of times where you have the need to save a figure in a specific format and integrate it with some other presentation.

Matplotlib supports many different formats for saving files. You can use fig.canvas.get_supported_filetypes() to see what your system supports:

fig.canvas.get_supported_filetypes()
{'eps': 'Encapsulated Postscript',
 'jpeg': 'Joint Photographic Experts Group',
 'jpg': 'Joint Photographic Experts Group',
 'pdf': 'Portable Document Format',
 'pgf': 'PGF code for LaTeX',
 'png': 'Portable Network Graphics',
 'ps': 'Postscript',
 'raw': 'Raw RGBA bitmap',
 'rgba': 'Raw RGBA bitmap',
 'svg': 'Scalable Vector Graphics',
 'svgz': 'Scalable Vector Graphics',
 'tif': 'Tagged Image File Format',
 'tiff': 'Tagged Image File Format'}

Since we have the fig object, we can save the figure using multiple options:

fig.savefig('sales.png', transparent=False, dpi=80, bbox_inches="tight")

This version saves the plot as a png with opaque background. I have also specified the dpi and bbox_inches="tight" in order to minimize excess white space.

Conclusion

Hopefully this process has helped you understand how to more effectively use matplotlib in your daily data analysis. If you get in the habit of using this approach when doing your analysis, you should be able to quickly find out how to do whatever you need to do to customize your plot.

As a final bonus, I am including a quick guide to unify all the concepts. I hope this helps bring this post together and proves a handy reference for future use.

Matplotlib example

April 25, 2017 12:15 PM


Django Weblog

DjangoCon Europe 2017 in retrospect

DjangoCon Europe 2017 upheld all the traditions established by previous editions: a volunteer-run event, speakers from all sections of the community and a commitment to stage a memorable, enjoyable conference for all attendees.

Held in a stunning Art Deco cinema in the centre of the city, this year's edition was host to over 350 Djangonauts.

The team of always-smiling and willing volunteers, led by Emanuela Dal Mas and Iacopo Spalletti under the auspices of the Fuzzy Brains association, created a stellar success on behalf of all the community.

Of note in this year's conference was an emphasis on inclusion, as expressed in the conference's manifesto. The organisers' efforts to expand the notion of inclusion was visible in the number of attendees from Africa and south Asia, nearly all of whom were also given a platform at the event. This was made possible not only by the financial assistance programme but also through the considerable logistical help the organisers were able to offer.

The conference's opening keynote talk by Anna Makarudze and Humphrey Butau on the growing Python community in Zimbabwe, and an all-woman panel discussing their journeys in technology, were just two examples of a commitment to making more space for voices and stories that are less often heard.

DjangoCon Europe continues to thrive and sparkle in the hands of the people who care about it most, and who step forward each year as volunteers who commit hundreds of hours of their time to make the best possible success of it. Once again, this care has shone through.

On behalf of the whole Django community, the Django Software Foundation would like to thank the entire organising team and all the other volunteers of this year's DjangoCon Europe, for putting on a superb and memorable production.

The next DjangoCons in Europe

The DSF Board is considering bids for DjangoCon Europe 2018-2020. If you're interested in hosting the event in one of these years, we'd like to hear from you as soon as possible.

April 25, 2017 09:05 AM


PyBites

How to Write a Simple Slack Bot to Monitor Your Brand on Twitter

In this article I show you how to monitor Twitter and post alerts to a Slack channel. We built a nice tool to monitor whenever our domain gets mentioned on Twitter. The slacker and twython modules made this pretty easy. We also use configparser and logging.

April 25, 2017 09:00 AM


S. Lott

Modules vs. Monoliths vs. Microservices:

Dan Bader (@dbader_org)
Worth a read: "Modules vs. Microservices" (and how to find a middle ground) oreilly.com/ideas/modules-…

"don't trick yourself into a microservices-only mindset"

Thanks for sharing.

The referenced post gives you the freedom to have a "big-ish" microservice. My current example has four very closely-related resources. There's agony in decomposing these into separate services. So we have several distinct Python modules bound into a single Flask container.

Yes. We lack the advertised static type checking for module boundaries. The kind of static type checking that doesn't actually solve any actual problems, since the issues are always semantic and can only be found with unit tests and integration tests and Gherkin-based acceptance testing (see Python BDD: https://pypi.python.org/pypi/pytest-bdd and https://pypi.python.org/pypi/behave/1.2.5).

We walk a fine line. How tightly coupled are these resources? Can they actually be used in isolation? What do the possible future changes look like? Where is the swagger.json going to change?

It's helpful to have both options on the table.

April 25, 2017 08:00 AM


Daniel Bader

Sets and Multisets in Python

Sets and Multisets in Python

How to implement mutable and immutable set and multiset (bag) data structures in Python using built-in data types and classes from the standard library.

A set is an unordered collection of objects that does not allow duplicate elements. Typically sets are used to quickly test a value for membership in the set, to insert or delete new values from a set, and to compute the union or intersection of two sets.

In a “proper” set implementation, membership tests are expected to run in O(1) time. Union, intersection, difference, and subset operations should take O(n) time on average. The set implementations included in Python’s standard library follow these performance characteristics.

Just like dictionaries, sets get special treatment in Python and have some syntactic sugar that makes it easier to create sets. For example, the curly-braces set expression syntax and set comprehensions allow you to conveniently define new set instances:

vowels = {'a', 'e', 'i', 'o', 'u'}
squares = {x * x for x in range(10)}

Careful: To create an empty set you’ll need to call the set() constructor, as using empty curly-braces ({}) is ambiguous and will create a dictionary instead.

Python and its standard library provide the following set implementations:

✅ The set Built-in

The built-in set implementation in Python. The set type in Python is mutable and allows the dynamic insertion and deletion of elements. Python’s sets are backed by the dict data type and share the same performance characteristics. Any hashable object can be stored in a set.

>>> vowels = {'a', 'e', 'i', 'o', 'u'}
>>> 'e' in vowels
True

>>> letters = set('alice')
>>> letters.intersection(vowels)
{'a', 'e', 'i'}

>>> vowels.add('x')
>>> vowels
{'i', 'a', 'u', 'o', 'x', 'e'}

>>> len(vowels)
6

✅ The frozenset Built-in

An immutable version of set that cannot be changed after it was constructed. Frozensets are static and only allow query operations on their elements (no inserts or deletions.) Because frozensets are static and hashable they can be used as dictionary keys or as elements of another set.

>>> vowels = frozenset({'a', 'e', 'i', 'o', 'u'})
>>> vowels.add('p')
AttributeError: "'frozenset' object has no attribute 'add'"

✅ The collections.Counter Class

The collections.Counter class in the Python standard library implements a multiset (or bag) type that allows elements in the set to have more than one occurrence.

This is useful if you need to keep track not only if an element is part of a set but also how many times it is included in the set.

>>> from collections import Counter
>>> inventory = Counter()

>>> loot = {'sword': 1, 'bread': 3}
>>> inventory.update(loot)
>>> inventory
Counter({'bread': 3, 'sword': 1})

>>> more_loot = {'sword': 1, 'apple': 1}
>>> inventory.update(more_loot)
>>> inventory
Counter({'bread': 3, 'sword': 2, 'apple': 1})

Careful with counting the number of elements in a Counter object. Calling len() returns the number of unique elements in the multiset, whereas the total number of elements must be retrieved slightly differently:

>>> len(inventory)
3  # Unique elements
>>> sum(inventory.values())
6  # Total no. of elements

📺🐍 Learn More With This Video Tutorial

I recorded a step-by-step video tutorial to go along with the article. See how sets work in general and how to use them in Python. Watch the video embedded below or on my YouTube channel:

» Subscribe to the dbader.org YouTube Channel for more Python tutorials.

Read the full “Fundamental Data Structures in Python” article series here. This article is missing something or you found an error? Help a brother out and leave a comment below.

April 25, 2017 12:00 AM

April 24, 2017


Andre Roberge

Easily modifiable Python

You want to try out some new syntactic construction in Python but do NOT want to have to go through the trouble of 1. modifying Python's grammar file 2. modifying Python's lexer 3. modifying Python's parser 4. modifying Python's compiler 5. re-compile all the sources  so as to create a new Python interpreter? (These steps are described in these two blog  posts.) It can be done much more easily

April 24, 2017 10:47 PM


François Dion

Meet Eliza #AI



I will be presenting and directing a discussion on artificial intelligence, from various angles including the arts, Tuesday April 25th at Wake Forest in Winston Salem, NC.

Details here:
http://www.pyptug.org/2017/04/pyptug-monthly-meeting-meet-eliza-april.html

Francois Dion
@f_dion

April 24, 2017 09:48 PM


NumFOCUS

Moore Foundation gives grant to support NumFOCUS Diversity & Inclusion in Scientific Computing initiatives

As part of our mission to support and promote better science through support of the open source scientific software community, NumFOCUS champions technical progress through diversity. NumFOCUS recognizes that the open source data science community is currently highly homogenous. We believe that diverse contributors and community members produce better science and better projects. NumFOCUS strives […]

April 24, 2017 07:23 PM


Weekly Python Chat

Python Oddities

I tweeted one Python oddity each week in 2016. Let's chat about them.

April 24, 2017 05:15 PM


Ned Batchelder

Shell = Maybe

A common help Python question: how do I get Python to run this complicated command line program? Often, the answer involves details of how shells work. I tried my hand at explaining it what a shell does, why you want to avoid them, how to avoid them from Python, and why you might want to use one: Shell = Maybe.

April 24, 2017 03:38 PM


NumFOCUS

Anyone Can Do Astronomy with Python and Open Data

Ole Moeller-Nilsson, CTO at Pivigo, was kind enough to share his insights on how a beginner can easily get started exploring astronomy using Python. This blog post grew out of a presentation he gave at PyData London meetup on March 7th. Python is a great language for science, and specifically for astronomy. The various packages […]

April 24, 2017 03:00 PM


Doug Hellmann

zlib — GNU zlib Compression — PyMOTW 3

The zlib module provides a lower-level interface to many of the functions in the zlib compression library from the GNU project. Read more… This post is part of the Python Module of the Week series for Python 3. See PyMOTW.com for more articles from the series.

April 24, 2017 01:00 PM


Mike Driscoll

PyDev of the Week: Honza Král

This week we welcome Honza Král (@HonzaKral) as our PyDev of the Week! Honza is one of the core developers of the Django web framework. He is also the maintainer of the official Python client to Elasticsearch. You can see some of the projects he is interested in or working on over at Github. Let’s spend some time getting to know Honza better!

Can you tell us a little about yourself (hobbies, education, etc):

I grew up in the Old Town of Prague, Czech Republic where I also went to school, including University where I studied computer science (which I didn’t finish). During my studies I discovered Python and immediately fell in love, first with the language and later, after going to my first Pycon in 2008, with the community.

I became a part of the Django community which was (and still is to this day) very welcoming. I became a part of it to learn and, hopefully, contribute something back. For my part it worked amazingly well – I got all my recent jobs through the community and even met my fiancee at a EuroPython conference!

Nowadays I work for Elastic, the company behind Elasticsearch where I do consulting – traveling around the world helping people be successful with open source – and also maintain the official Python client.

Why did you start using Python / Django?

I started in university for a project. I had my eye on Python for some time then and a perfect opportunity presented itself to use it for one of our team projects – we decided it would be a web-based application and Django was the shiny new thing that caught my attention. I introduced it to the rest of the team and have been using it ever since.

What other programming languages do you know and which is your favorite?

My other favorite, besides Python of course :), would be shell – I still enjoy writing both simple (or a bit more complex) one-liners as well as longer scripts in shell. I like the variety of it where you have so many possibilities on how to accomplish things with a seemingly limited language. I like how it’s very unlike other languages I am used to working in as it forces you to think more in terms of pipelines and “just” passing off problems to another app/language. It definitely helped my Python skills as well I think – thinking of what I need to keep in a list/variable vs where a simple generator would suffice (as would a pipeline in shell) etc.

Right now I am looking at rust and hoping that a suitable project will come along for me to give it a serious try.

What projects are you working on now?

Currently I am working on elasticsearch-dsl which is an ORM-like abstraction on top of elasticsearch. It is an ambitious project since I wanted to expose all the power of elasticsearch’s query DSL while also trying to make it more accessible to non-experts and generally friendlier. While I think that in some areas I might have achieved this, I still have a long way to go in others; and for some I don’t even see the clear path of what needs to happen which makes it interesting – gives me something to ponder 🙂

Which Python libraries are your favorite (core or 3rd party)?

As a 3rd party library I really like click because of how well it fulfills its purpose. From the standard library I am a huge fan of itertools, collections and other “helper” libraries that don’t just offer some functionality but also, and often more importantly, show how a real pythonic code should look like and makes your code more pythonic when you use them properly – the same quality I always liked about Django where it guides you in the right direction and makes the right-way to things so obvious that it trains you to recognize it and use it also outside of your Django projects.

Another library I find really interesting is py.test – I love the feature set and how easy and flexible it makes testing but I am also a bit wary of the “magic” that happens there. To me that is a perfect example of the pragmatism in the python community – I think we can all accept a little magic as long as it is clearly defined and serves its purpose.

Is there anything else you’d like to say?

I want to say a huge Thank you to the global Python community out there! I have been privileged enough to see a huge part of it and it still manages to catch me off guard regularly with the amount of awesomeness and caring that make for a great environment. I interact with developers all over the world as part of my job and really appreciate the amount effort and thought the people in the Python community are putting into making sure it is a thriving and welcoming one.

Thanks so much for doing the interview!

April 24, 2017 12:30 PM


Import Python

Import Python Weekly Issue 122 - Django ORM vs SQLAlchemy, Type annotation, websockets and more

Worthy Read

Recently I started using SQLAlchemy and am very impressed with it. I have used Django ORM a lot in the past. This post compares achieving same result using Django and with SQLAlchemy. Let’s see which looks more intuitive.
django
,
SQLAlchemy
,
ORM

Users finding bugs? Searching logs for errors? Find + fix broken code fast! You can find the Python integration here - https://rollbar.com/docs/notifier/pyrollbar/
sponsor

In this part, we will implement graph data structure using classes and interfaces, and discuss when it’s worth overruling type hints.
type annotation
,
graph

I recently I had the opportunity to speed up some Websocket code that was a major bottleneck. The final solution was 60X (!) faster than the first pass, and an interesting exercise in optimizing inner loops.
websockets

This trouble to visualise the relationship between entities in a Relational Database is a great reason to introduce the concept of graph. Graph is a data structure formed by a set of vertices V and a set of edges E. It can be represented graphically (where the vertices are shown as circles and edges are shown as lines) or mathematically in the form G = (V, E).
graph databases

This week I raised my first pull-request to the CPython core project, which was declined :-( but as to not completely waste my time I’m writing my findings on how CPython works and show you how easy it is to modify the Python syntax.
core-python
,
cpython

Nchan makes writing realtime web based pub/sub applications a breeze. In this article, we will build a simple systems monitoring dashboard which displays process information in realtime similar to what you would see when you run the unix top or htop commands.
nginx
,
nchan

Suppose you generate several sets on the fly, and you want to find the elements that are in all the sets. That's easy, it's the intersection of sets.
list
,
set
,
code_snippets

I was working recently on writing Python code using TDD. So every time I change code I wanted to run command that will test my code if it pass unit tests. In order to do that I needed some service/app that will monitor for file changes, and if it changes execute my batch file that runs unit tests.
python
,
watchdog

We help companies like Airbnb, Pfizer, and Artsy find great developers. Let us find your next great hire. Get started today.
sponsor

jupyter

aws
,
lamda

In this session you will learn how SQL Server 2017 takes in-database analytics to the next level with support for both Python and R; delivering unparalleled scalability and speed with new deep learning algorithms built in.
sqlserver

core-python

kubernetes

podcast


Projects

algorithms: - 2112 Stars, 174 Fork
Minimal examples of data structures and algorithms in Python

jokekappa - 74 Stars, 7 Fork
A library for delivering one-line programming jokes

cashier - 53 Stars, 2 Fork
Persistent caching for python functions

crickbuzz_cricket_score - 13 Stars, 2 Fork
A Terminal based program to follow live cricket score by scraping crickbuzz.com

MigrateGitlabToGogs - 3 Stars, 0 Fork
Migrate repositories from Gitlab to Gogs or Gitea

venvdetect - 3 Stars, 1 Fork
Detect available Python virtual environments in your current directory

April 24, 2017 12:11 PM


Django Weekly

Django Weekly 35 - Signals, Django Rest Framework, Djangocon 2017

Worthy Read

In many cases when there is a modification in a model’s instance we need execute some action. Django provides us an elegant way to handle with these situations. The signals are utilities that allow us to associate events with actions. We can develop a function that will run when a signal calls it.
signals

Users finding bugs? Searching logs for errors? Find + fix broken code fast!. You can find the Python integration here - https://rollbar.com/docs/notifier/pyrollbar/
sponsor

Sort of a learning path made up of curated articles.
DRF

One of our tables grew to over 25 million rows. Which, while not a large number, makes the most frequent queries slow. A direct effect it had was on our Admin panel. The change_list form for the model started taking ages to load.
admin

I love DjangoCon. I've been going to it almost every year since I arrived in Europe back in 2010. Sure, a considerable portion of my career has been based on Django, but it's more than that: the community is stuffed full of amazing people who genuinely want us all to succeed and that just makes the conference all the more exciting.
djangocon

We help companies like Airbnb, Pfizer, and Artsy find great developers. Let us find your next great hire. Get started today.
sponsor

installation

DjangoCon US is a six-day international conference for the community by the community, held each year in North America, about the Django web framework. Early bird tickets avialable.
django
,
djangocon


Projects

django-webpacker - 18 Stars, 1 Fork
A django compressor tool that bundles css, js files to a single css, js file with webpack and updates your html files with respective css, js file path.

django-starter-kit - 12 Stars, 0 Fork
Opinionated template for Django projects on Python 3 and PostgreSQL.

mnm - 9 Stars, 0 Fork
Mastodon Network Monitoring: track and display browsable stats about Mastodon network (instances, toots, users...).

django-multicore - 4 Stars, 0 Fork
A framework that makes it easy to parallelize Django code

django-freeradio - 4 Stars, 4 Fork
A Django project for managing a radio station for a community radio station. The code was originally based on the Django website for Brum Radio, and was open sourced as part of a sprint session at DjangoCon Europe 2017.

django-confirmation-mixin - 3 Stars, 0 Fork
Django ConfirmationMixin for CreateView and UpdateView

April 24, 2017 12:09 PM


eGenix.com

PyDDF Python Spring Sprint 2017

The following text is in German, since we're announcing a Python sprint in Düsseldorf, Germany.

Ankündigung

PyDDF Python Frühlings-Sprint 2017 in
Düsseldorf


Samstag, 06.05.2017, 10:00-18:00 Uhr
Sonntag, 07.05.2017, 10:00-18:00 Uhr

trivago GmbH,  Karl-Arnold-Platz 1A,  40474 Düsseldorf

Informationen

Das Python Meeting Düsseldorf (PyDDF) veranstaltet mit freundlicher Unterstützung der trivago GmbH ein Python Sprint Wochenende im Mai.

Der Sprint findet am Wochenende 6./7.5.2017 in der trivago Niederlassung am Karl-Arnold-Platz 1A statt (nicht am Bennigsen-Platz 1). Folgende Themengebiete haben wir als Anregung angedacht:
Openpyxl ist eine Python Bibliothek, mit der man Excel 2010+ Dateien lesen und schreiben kann.

Charlie Clark ist Co-Maintainer des Pakets.

Telegram ist eine Chat-Anwendung, die von vielen Nutzern verwendet wird. Telegram unterstützt das Registrieren von sogenannten Bots - kleinen Programmen, die man vom Chat aus ansteuern kann, um z.B. Informationen zu bekommen.

Im Sprint wollen wir versuchen, einen Telegram-Bot in Python zu schreiben.

Natürlich kann jeder Teilnehmer weitere Themen vorschlagen, z.B.

Anmeldung und weitere Infos

Alles weitere und die Anmeldung findet Ihr auf der Sprint Seite:

Teilnehmer sollten sich zudem auf der PyDDF Liste anmelden, da wir uns dort koordinieren:

Über das Python Meeting Düsseldorf

Das Python Meeting Düsseldorf ist eine regelmäßige Veranstaltung in Düsseldorf, die sich an Python Begeisterte aus der Region wendet.

Einen guten Überblick über die Vorträge bietet unser PyDDF YouTube-Kanal, auf dem wir Videos der Vorträge nach den Meetings veröffentlichen.

Veranstaltet wird das Meeting von der eGenix.com GmbH, Langenfeld, in Zusammenarbeit mit Clark Consulting & Research, Düsseldorf.

Viel Spaß !

Marc-Andre Lemburg, eGenix.com

April 24, 2017 11:00 AM


EuroPython

EuroPython 2017: How you can sponsor us

Check out our brand new brochure with all details about our sponsor packages, options and lounge events, available on our website.

Sponsoring EuroPython guarantees you highly targeted visibility and the opportunity to present yourself and your company in a professional and innovative environment. 

image


With more than 1200 participants, 8 days conference program, international keynotes, more than 200 talks, training sessions for beginners, advanced and experts, EuroPython creates an excellent environment to look for new job-opportunities, recruit people, connect, share and learn. 

New Attractive Location for 2017

This year we move to the Palacongressi in Rimini, the largest convention centre in Italy, unique in its design and technology, with 11,000 m² exhibit space, in addition to 39 conference rooms. Check out EuroPython demographics, refresh your mind with our beautiful location and consult the different opportunities we have created for you

Available Sponsor Options

We offer 7 different sponsor packages, with different levels of engagement.

Would you rather have more flexibility? You can separately book special events and choose from a list of very prominent advertisement options: have a table in the training session or get your logo to be by the ice-cream stall; sponsor our laser-tag and party events. 

Do you have further ideas? We can help you develop a tailored sponsorship package that fits your specific targets. If you would like to engage and sponsor EuroPython 2017, please contact us by e-mail.

Don’t miss the chance to become part of the largest Python conference in Europe!

Enjoy,

EuroPython 2017 Team
http://ep2017.europython.eu/
http://www.europython-society.org/

April 24, 2017 08:54 AM

April 23, 2017


PythonClub - A Brazilian collaborative blog about Python

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:

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".

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

April 23, 2017 11:37 PM


Evennia

The luxury of a creative community

For this blog post I want to focus on the series of very nice pull requests coming in from a growing cadre of contributors over the last few months.

Contributed goodness

People have put in a lot of good work to boost Evennia, both by improving existing things and by adding new features. Thanks a lot everyone (below is just a small selection)!
About that Evennia 0.7 ...

So while PRs are popping up left and right in master I've been working in the devel branch towards what will be the Evennia 0.7 release. The branch is not ready for public consumption and testing yet But tentatively it's about halfway there as I am slowly progressing through the tickets. Most of the upcoming features were covered in the previous blog post so I'll leave it at that.

I just want to end by saying that it's a very luxurious (and awesome) feeling for me to see master-branch Evennia expand with lots of new stuff "without me" so to speak. The power of Open Source indeed!
  

Image from http://maxpixel.freegreatpicture.com, released as public domain.

April 23, 2017 11:10 PM