4 Melhores Podcasts sobre Tecnologia e Startups

Ouvir Podcasts é uma forma muito eficiente para se manter por dentro do que acontece na sua área.  Ouço vários Podcasts sobre tecnologia e startups, além de outros temas e tenho certeza que eles são importantes para que eu continue atualizado no meu ramo. Estar informado pode ser a diferença entre ser bom e ser ótimo.

Para quem não conhece, podcast é como um programa de rádio com a diferença de que você pode ouvir quando quiser, escolher os episódios e sempre ficará sabendo quando publicarem um programa novo. Podcast não é novidade mas não chegou a virar mainstream no Brasil ainda. Vai crescer muito este ano.

Meu aplicativo para assinar (baixar e ouvir) podcasts é o PocketCasts ele é pago, paga uma vez só e usa todos os dias, mas existem várias outras opções gratuitas aí para quem não quiser desembolsar os R$ 12 reais. Bora pra lista:

Hipsters.tech

logo do podcast hipsters.tech

Apesar do nome péssimo, clichê e modinha é na minha opinião o melhor podcast brasileiro. Tem um episódio por semana e é um programa muito bem formatado. Normalmente os episódios tem menos de uma hora e variam sobre desenvolvimento, design, empreendedorismo, etc.

Esse é um dos episódios que gostei bastante sobre Squads. Você pode dar uma olhada em outros episódios no site deles https://hipsters.tech

Software Engineering Daily

software engineering daily logo

Esse é um podcast gringo (em inglês). Eles soltam um episódio por dia útil, ou seja, é daily mesmo. Mesmo que não dê para ouvir todos os dias é bom ficar por dentro e olhar o que eles já fizeram. Tem entrevistas com gente da área de tecnologia e de startups, normalmente engenheiros e empreendedores de empresas renomadas e criadores das tecnologias que estão sendo discutidas. Você pode ver os episódios aqui

Talk Python to Me

talk python to me podcast logo

Esse podcast é mais específico para pythonistas, mas quem não gosta de python? Tem participações de caras muito fodas da comunidade como Kenneth Reitz, David Beasley e até do Guido Van Rossum.

O site é esse aqui: https://talkpython.fm/ . Meu único problema com esse podcast é que ele tinha a melhor música de abertura de qualquer podcast até o meio do ano passado, depois entrou uma música chata =P

Like a Boss

like a boss podcast logo

Esse é um podcast dos mesmos criadores do Hipsters. O objetivo é “trazer entrevistas com líderes, fundadores de startups e empresas inovadoras” nas próprias palavras deles. Está bem no comecinho, tem apenas 7 episódios.

Gostei bastante dessa entrevista com David Vélez, o fundador do Nubank.

Outros podcasts:

Eu gostava muito do ZOFE (Zone of Front-Enders) , mas infelizmente ele não tem novas publicações já faz tempo. O site está mais desatualizado que o podcast mas ainda assim dá para ouvir conteúdo do passado. Era realizado pelo Daniel Filho, um cara diferenciado que eu sempre via no melhor meetup de front-ends de São Paulo, o FEMUG-SP.

Falando em podcasts antigos, outro que curti bastante mas já não solta coisa nova é o Grok Podcast. Isso que é bom dos podcasts, eles podem ter acabado mas os episódios estão aí para sempre.

Um podcast que eu descobri recentemente foi o Castálio Podcast. Também fala bastante de Python. Por enquanto, só ouvi um episódio e foi sobre serverless. Aliás, dá pra ver o podcast sendo gravado ao vivo no youtube e mandar perguntas para eles.

Mais um podcast gringo que estou esperando ver como vai desenrolar é o Modern CTO. Me parece uma estratégia audaciosa para criar conteúdo para CTOs uma coisa que não se vê todo dia. Estou começando a acompanhar.

Bom, esses foram os meus podcasts favoritos sobre esses temas. Quem tiver outros podcasts que quiser indicar deixe nos comentários.

 

Guia para Deploy Django Python 3

crianças fazendo deploy

Tutorial de Deploy por aí é o que não falta, a maioria em inglês. Esse que estou criando é pra engrossar o caldo de deploys em português. Esse é um Guia Definitivo Rápido, ou não tão rápido, para fazer Deploy Django com Python 3. É um deploy para Kids.

A dificuldade de fazer um deploy reside nos detalhes. No fundo é fácil se você está familiarizado com as partes envolvidas. Você precisa saber fazer uma autenticação ssh, estar acostumado com a linha de comando, conhecer linux, saber configurar o projeto, entender o que é servir arquivos estáticos, gunicorn…. tá, tá… nunca é fácil e muito menos rápido, justamente por isso criaram um monte de ferramentas pra deploy. E hoje com Ansible, Docker e whatever kids are using these days fica fácil fazer o deploy mas muito abstrato entender o funcionamento.

Em alguns anos esse post será obsoleto pra sempre, com serverless e tudo mais acho que pouca gente vai querer saber como fazer um deploy django dessa forma. Mas, mesmo assim, se ajudar uma pessoa já está bom. Será um tutorial Old-Style.

O Servidor

Vou imaginar que você não tem um servidor, não tem uma conta na AWS, nem DigitalOcean, nem Linode, nada… Você pode criar uma conta em um deles, lançar uma máquina com as configurações que quiser (Na AWS é tudo mais complicado pra quem não está acostumado, se for sua primeira vez, prefira outro).

Pra esse tutorial estou falando de Ubuntu 16.04, que é o servidor que você mais vai ver por aí nesse momento nesses serviços. Você pode escolher um Debian qualquer também.

Configuração inicial

Configure o timezone do servidor

sudo locale-gen --no-purge --lang pt_BR
sudo dpkg-reconfigure tzdata

Atualize os pacotes:

sudo apt-get update 
sudo apt-get -y upgrade

Instalando Python 3.6 no lugar do Python 3.5

Agora substitua o Python 3.5 instalado, pelo Python3.6 (O Ubuntu que indiquei, ele vem com Python 3.5.1)

sudo apt-get update
sudo add-apt-repository ppa:jonathonf/python-3.6
sudo apt-get install python3.6
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 2

Você pode escolher qual versão do Python o SO vai usar ao chamar python3 com:

sudo update-alternatives --config python3

Se você se enrolar, dê uma olhada aqui.

Instale os requisistos do Sistema Operacional

Aqui tem alguns pacotes que eu sempre uso em um deploy.

sudo apt-get install python3-pip nginx supervisor git git-core libpq-dev python-dev 
python-virtualenv

Seu projeto pode ter outros requirements do SO pra instalar.

VirtualEnvWrapper para o Python3

Eu gosto muito do VirtualEnvWrapper, acho simples de começar um virtualenv, e deixa todos os meus virtualenvs no mesmo lugar, mas isso é escolha pessoal, se você não gosta, faça como está acostumado

Você instala o virtualenvwrapper, depois define a pasta dos seus virtualenvs (WORKON_HOME).

Para usar com múltiplos Pythons você vai precisar definir VIRTUALENVWRAPPER_PYTHON. No caso estou usando sempre o padrão que defini para o comando python3. Isso não é um problema porque você pode criar um virtualenv depois apontando qual python aquele virtualenv vai usar.

sudo pip3 install virtualenvwrapper
echo 'export WORKON_HOME=~/Envs' >> ~/.bashrc
echo ‘export VIRTUALENVWRAPPER_PYTHON=`which python3`’ >> ~/.bashrc
echo 'source /usr/local/bin/virtualenvwrapper.sh' >> ~/.bashrc
source ~/.bashrc

Crie seu VirtualEnv apontando qual python aquele virtualenv irá usar

mkvirtualenv nome_venv --python=python3

O VirtualEnvWrapper é muito fácil, para entrar em um Virtualenv que você criou, você pode usar:

workon nome_venv

Para sair do virtualenv:

deactivate

Para excluir um virtualenv:

rmvirtualenv nome_venv

Gere as Chaves SSH para autenticar no GitHub

Você não quer (e nem deveria) escrever a senha pra fazer o git pull do seu projeto no servidor.

Gere as chaves SSH

cd ~/.ssh
ssh-keygen -t rsa -b 4096 -C "[email protected]"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa

Veja e copie o conteúdo da sua chave pública (id_rsa.pub)

cat ~/.ssh/id_rsa.pub

Depois entre no seu github, em Settings > SSH and GPG Keys. Clique em New SSH Key, dê um nome pra essa chave, como (“chaves do servidor teste”) e em Key cole o conteúdo da chave pública id_rsa.pub

Faça o clone do seu projeto Django

Copie o link SSH do Github para fazer o clone, no caso estou usando um projeto que encontrei agora pra exemplo

git clone [email protected]:kirpit/django-sample-app.git

Entre na pasta do projeto e instale os requirements do projeto.

Lembre-se de estar no virtual env correto.

cd django-sample-app/
pip install -r requirements.txt

Agora faça as alterações que forem necessárias para o deploy django, como criar um arquivo de settings_local, banco de dados, ou qualquer outra coisa específica do seu projeto.

Depois de tudo certo, você ainda precisa rodar as migrações e gerar os arquivos estáticos (se estiver usando)

python manage.py migrate
python manage.py collectstatic

Configurando o NGINX

Nginx, assim como o Apache, tem um mundo inteiro só deles, nesse momento você precisa conhecer o básico.

Existe um diretório /etc/nginx/sites-available/ onde ficão os arquivos de configuração de sites disponíveis para o nginx servir e existe o diretório /etc/nginx/sites-enabled/ que é a mesmíssima coisa, mas o que estiver aqui o Nginx estará servindo mesmo.

Normalmente você cria o arquivo de configuração no sites-available/ e cria um link simbólico para o sites-enabled/

Nós vamos fazer isso. Primeiramente, vou excluir o site default do nginx

sudo rm /etc/nginx/sites-enabled/default

Agora crie o arquivo de configuração para o seu site. (Se você não está acostumado com o VIM, use troque vi por nano)

sudo vi /etc/nginx/sites-available/meusite

No conteúdo do arquivo, coloque isto, mudando os caminhos necessários:

server {
 listen 80;
 access_log /home/usuario/logs/access.log;
 error_log /home/usuario/logs/error.log;

 server_name nome-site.com.br;

 location / {
 proxy_pass http://127.0.0.1:8000; 

 #As proximas linhas passam o IP real para o gunicorn nao achar que sao acessos locais
 proxy_pass_header Server;
 proxy_set_header X-Forwarded-Host $server_name;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header Host $http_host;


 }

 location /static {

   alias /home/usuario/caminho_projeto/static/;

 }

}

Agora crie o link simbólico para o sites-enabled:

sudo ln -s /etc/nginx/sites-available/meusite /etc/nginx/sites-enabled/meusite

Reinicie o Nginx:

sudo service nginx restart


(Se você configurou tudo direitinho até aqui, ao acessar o site você verá uma página com um erro 502 Bad Gateway do próprio nginx)

Isso acontece porque ainda não tem nada aqui http://127.0.0.1:8000

Vamos colocar o site pra rodar nessa porta pelo gunicorn

Configurando o Gunicorn

Alguém vivo até essa parte? Cansa não, falta pouco.

No seu virtualenv (lembra workon nome_env?) instale o gunicorn

pip install gunicorn

Na pasta do seu projeto crie um arquivo chamado gunicorn_conf com:

bind = "127.0.0.1:8000"
logfile = "/home/usuario/logs/gunicorn.log"
workers = 3

Agora se você rodar o gunicorn, você vai ver seu site rodando:

/home/usuario/Envs/nome_venv/bin/gunicorn projeto.wsgi:application -c gunicorn_conf

Mas o que você pretende fazer? Rodar esse comando dentro de um screen e ir embora? Não dá né! Então, você vai usar o Supervisor pra controlar o funcionamento do gunicorn.

Configurando o Supervisor

Crie o seguinte arquivo de configuração

sudo vi /etc/supervisor/conf.d/gunicorn.conf

Com o seguinte conteúdo:

[program:gunicorn]
command=/home/usuario/Envs/nome_venv/bin/gunicorn projeto.wsgi:application -c /home/usuario/projeto/projeto_django/gunicorn_conf
directory=/home/usuario/projeto/projeto-django
user=usuario
autostart=true
autorestart=true
redirect_stderr=true

Depois é só avisar o supervisor que existe um novo processo que ele precisa controlar da seguinte forma:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart gunicorn

E voilá! Um site rodando você terá!

Conclusão

Existem muito mais coisas envolvidas no processo de um deploy. Você precisa configurar um firewall, provavelmente precisará servir mais pastas estáticas, etc, etc, etc… Mas precisa começar por algum lugar.

Não acredito que fiz um post inteiro sem colocar nenhum gif no meio, então só pra terminar, PRESTE ATENÇÃO em TODOS os caminhos que eu coloquei acima, você vai ter que usar os seus próprios paths corretamente

Oops..

Populando um campo novo não-nulo no Django

Fala pessoal, esse é um post rápido no qual eu vou mostrar como popular uma migração criando um campo novo não-nulo no django

SAY WUUUUT?????

george costanza taking his glasses off

Seguinte, sabe quando você tem seu projeto rodando, banco de dados, models, front, em produção as porra tudo e então aparece um requisito novo e eis que surge um campo obrigatório, que ninguém pensou antes, nem o cliente, nem o product owner, nem ninguém! Essa é a situação!

Acontece que você usa as migrações do django e você quer que poder colocar esses campos usando os migrations, tanto migrando pra frente quanto pra trás, ok?

Chega de conversa, pra esse exemplo resolvi pegar um projeto pronto nas interwebz, acabei optando por um django polls feito em django 1.10 já pronto.

Então lá se vão os passos de sempre, clonar e criar virtualenv…

git clone [email protected]:garmoncheg/django-polls_1.10.git
cd django-polls_1.10/
mkvirtualenv --python=/usr/bin/python3 django-polls 
pip install django==1.10  # Nesse projeto o autor não criou um requirements.txt
python manage.py migrate  # rodando as migrações existentes
python manage.py createsuperuser 
python manage.py runserver

Obs: Esse projeto pronto que pegamos está com uma migração faltando, então se você estiver seguindo esse passo a passo rode um python manage.py makemigrations pra que seja criada a migração 0002 (que é só a mudança de um verbose_name)

Agora você acessa o admin (http://localhost:8000/admin/polls/question/add/) e cria o seu poll :

 

django polls admin
Aí você pode ir lá no app polls e ver sua pergunta, responder etc…
até aí OK, ainda não fizemos nada!

Bom, o ideal é criar mais umas perguntas com datas de publicação diferentes para a brincadeira
ficar boa.

Depois de começar a usar você vai perceber que qualquer enquete fica infinita no seu site, ou seja, todas as vezes que alguém entra no site ele tem a oportunidade de votar, você nunca encerra a votação.

Então nossa alteração no código será a seguinte: De agora em diante, todas as enquetes terão uma data de expiração. No momento do cadastro o usuário poderá colocar a data na qual a votação se encerrará e o usuário será direcionado diretamente para o resultado. Queremos que essa data de expiração seja um campo obrigatório! E para os que já estão funcionando na plataforma vamos determinar arbitrariamente que cada um tenha um mês de validade a partir da data de publicação.

Antigamente, antes de migrações como isso era feito em qualquer sistema? Via SQL você criava um campo que permitisse NULOS, depois você criava uma query que populava esse campo e por fim você alterava a tabela pra tornar aquela coluna obrigatória. Com as migrações é a mesma coisa.

Então vamos criar o campo novo nos models vou chamá-lo de expires_date:

expires_date = models.DateTimeField(‘expires at’, null=True)

E o model inteiro fica assim:

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    expires_date = models.DateTimeField('expires at', null=True)

    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'


Agora vamos criar a migração dessa mudança:

python manage.py makemigrations

E ele criará a migração 0003_question_expires_date. O conteúdo é o seguinte:

class Migration(migrations.Migration):

    dependencies = [
        ('polls', '0002_auto_20170429_2220'),
    ]

    operations = [
        migrations.AddField(
            model_name='question',
            name='expires_date',
            field=models.DateTimeField(null=True, verbose_name='expires at'),
        ),
    ]

 

Vamos modificar o código dessa migration, NO PANIC!

Populando o novo campo

Primeiro criamos uma função para popular o banco com as datas de expiração:

def populate_expires_date(apps, schema_editor):
    """
    Popula o campo data de expiração das perguntas já existentes colocando um mẽs de validade para cada.
    """
    from datetime import timedelta

    db_alias = schema_editor.connection.alias
    Question = apps.get_model('polls', 'Question')

    for row in Question.objects.using(db_alias).filter(expires_date__isnull=True):
        row.expires_date = row.pub_date + timedelta(days=30)
        row.save()

Originalmente usei esse código em um projeto que utiliza múltiplos bancos de dados, então precisei usar o db_alias e achei interessante deixá-lo aqui. Quanto ao Question, estou dando um import desse model usando o apps.get_model pois nesse momento que estamos rodando a migração o campo ainda não existe para o projeto, pois a migração não acabou de rodar, então é melhor que importar do model.

Agora, dentro da migração existe uma lista chamada operations. Nessa lista vamos adicionar os comandos para rodar a nossa função e em seguida, vamos adicionar a obrigatoriedade do campo: ficando dessa forma:

operations = [
    migrations.AddField(
        model_name='question',
        name='expires_date',
        field=models.DateTimeField(null=True, verbose_name='expires at'),
    ),
    migrations.RunPython(populate_expires_date, reverse_code=migrations.RunPython.noop),
    migrations.AlterField(
        model_name='question',
        name='expires_date',
        field=models.DateTimeField(verbose_name='expires at'),
    )
]

Você pode ver que utilizamos o migrations.RunPython para rodar nossa função durante a migração. O reverse_code serve quando alguém for dar um unapply da migração, nesse caso não existia o campo, então não faremos nada.

Logo em seguida adicionei a migração que altera o campo e ele deixa de ter null=True. Também poderíamos ter feito isso em outra migração, só retirando essa informação do model (que precisa ser retirada agora de qualquer forma) e ter criado uma nova migração.

O model ficará assim:

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    expires_date = models.DateTimeField('expires at')

    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'

agora você pode rodar o migrate tranquilamente:

python mange.py migrate

Pronto! Pra ver as alterações vou adicionar esse campo no admin, tanto para ser editado como no list_display:

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date', 'expires_date'], 'classes': ['collapse']}),
    ]
    inlines = [ChoiceInline]
    list_display = ('question_text', 'pub_date', 'expires_date', 'was_published_recently')
    list_filter = ['pub_date']
    search_fields = ['question_text']

E voilá, todos as Questions que você tinha no polls contam com um expires_date, obrigatório e com 30 dias adicionado por default para as Questions antigas.

É isso aí, agora tem esse campo que queremos! O projeto com as alterações está aqui: https://github.com/ffreitasalves/django-polls_1.10

Se gostou, compartilhe com os amigos e deixe um comentário. Se não gostou também! Abraços!

 

Fontes:

http://stackoverflow.com/questions/28012340/django-1-7-makemigration-non-nullable-field

http://stackoverflow.com/questions/29217706/django-view-sql-query-without-publishinhg-migrations

https://realpython.com/blog/python/data-migrations/

https://docs.djangoproject.com/en/1.10/howto/writing-migrations/#non-atomic-migrations

Por que não produzimos mais Ronaldinhos? Ou por trás dos highlights e a importância do feedback pra se tornar melhor.

Todos os dias milhares de crianças jogam bola pelas ruas. Correm o dia todo e só param quando suas mães gritam para elas irem pra casa pra comer. Se tem tanta gente praticando e treinando porque não aparecem mais Ronaldinhos, por aí?

Por trás dos Highlights

Hoje li esse lindo texto do Ronaldinho.  É uma carta pra ele mesmo quando criança em Porto Alegre.
Quando vemos um texto desses a gente presta muita atenção nos highlights. O momento em que ele é chamado pra jogar futebol no grêmio, depois pra jogar na sub 17, seleção brasileira, PSG, Barcelona, etc.  Mas aí no meio desse texto, por trás de todos esses highlights dá pra perceber o quanto esse cara treinou a vida toda. Desde criança jogava bola incessantemente e sempre com bons mentores.
Ronaldinho gaúcho e seu irmão Roberto
Pessoas falam sobre talento, dom ou predestinação, mas isso é superestimado. Alguém pode ter geneticamente mais facilidade para alguma coisa, sim, isso é um fato, vide Michael Phelps porém pra se tornar o melhor é só com muito treino e feedback o tempo todo.
A construção da carreira dessas pessoas de sucesso é diária. É como num filme sobre empreendedorismo, o cara tem uma ideia, forma uma equipe e eles começam a trabalhar juntos. Depois sempre vem aquela cena em fast motion, que mostra dias e noites passando enquanto eles rabiscam a lousa, mexem no computador, etc ¹… E essa é a parte importante da trajetória, todo esse trabalho feito. Isso não é glamouroso o bastante pra entrar no filme, mas é isso que proporciona o sucesso: trabalho ou treino
trabalhando-duro-com-jim-carrey
 
Por que não produzimos mais Ronaldinhos?
Se a prática / treino são tão importantes para garantir o sucesso, então porque não são todos os meninos que jogam bola o dia todo pelas ruas do Brasil que se tornam Ronaldinhos?

ronaldinho-driblando

 

Feedback: Uma breve história sobre MecFlu

Em 2007, no segundo ano de engenharia da Poli, eu cursei uma das matérias mais difíceis que fiz: Mecânica dos Fluidos. Muitos alunos já tinha me alertado que essa matéria era um terror. Que era tricky, que se não se dedicasse bastante não teria como passar.

Eu já não vinha de um semestre muito bom e resolvi estudar MecFlu (como chamávamos). Sempre fui do tipo autodidata e se o professor não for ultra didático tendo a acreditar que aprendo melhor com o livro do que na aula. Então peguei um dos livros recomendados, e li os capítulos relacionados à P1 (primeira prova), montei meu resumo, achei que tinha entendido e fiz todos os exercícios.

No dia da P1, tínhamos 1h40 pra resolver 4 exercícios. Resolvi todos e no final fui conversar com meus amigos…todos chorando as pitangas que tinham ido muito mal e eu não… falei pra eles que achava que tinha tirado um dez, que consegui resolver tudo sem muita dificuldade. Semanas depois saiu o resultado…meus amigos tinham ido mal como esperavam… notas: 3 ….. 2.5 …. 1 e eu tirei um grande e redondo ZERO. 

 

prova-de-mecflu-mecanica-dos-fluidos

A importância do feedback pra se tornar melhor

Como eu tirei esse zero tendo estudado e fazendo os exercícios? O livro não tinha respostas. Fiz os exercícios e considerei que os resultados estavam certos, logo não tive o feedback necessário pra ver o que eu precisava melhorar. O único feedback que tive foi o da prova. Já era tarde demais.

É esse o motivo pelo qual não produzimos mais Ronaldinhos. Não adianta você só passar o dia jogando futebol, você precisa ter o feedback se o que você está fazendo está certo. Também para aprender os atalhos e os pontos onde você pode melhorar.

Tem um episódio do Freakonomics Radio ótimo sobre isso, chama-se How to become Great at Just About Anything (tradução livre: Como se tornar FODA em praticamente tudo). Nesse episódio eles debatem as ideias do Malcolm Gladwell, autor de Outliers, (Aquele da regra das 10 mil horas) com os estudos do psicólogo Anders Ericsson, sobre como as pessoas adquirem expertise.

Um das coisas que mais me chamou a atenção foi o que hoje para você se classificar para a Maratona de Boston você precisa correr o equivalente ao de atletas das primeiras Olimpíadas. Em todas as olimpíadas quebramos recordes e em todos os campos parece que alcançamos mais em menos tempo. As pessoas aprendem mais, mais rapidamente e os humanos do futuro poderão ser mais rápidos que Bolt ou Phelps.

 

E os celeiro de Talentos?

Sabe aquela ideia de que muitos talentos vem do mesmo lugar?

  • O Barcelona é um celeiro de craques
  • O Vale do Silício é um celeiro de startups
  • A China é um celeiro de jogadores de ping-pong
  • e por aí vai…

Não é mágica. Cada um desses lugares tem um monte de pessoas (doravante mentores) com expertise pra que os ingressantes consigam apreender os truques para se tornarem melhores mais rapidamente e também possuem os recursos pra que as pessoas se desenvolvam.

Você pode aprender muita coisa sozinho, mas o feedback é super importante. Uma coisa boa de aprender programação é que no começo você pode evoluir muito rápido sozinho, afinal você escreve o software e na hora sabe se ele funcionou ou não. Rapidamente você vai aprendendo o que funciona e o que não funciona.

Claro que uma hora você vai precisar aprender formas mais fáceis e rápidas de programar. Trabalhando com outras pessoas você vai evoluir muito mais rápido. Por isso que em equipes que usam Code Review os programadores menos experientes evoluem melhor. Mesma coisa para quem contribui em projetos open source. Não necessariamente o mentor é um mentor de fato, mas alguém ou ferramenta que te mostre onde você errou e qual o melhor jeito de se fazer o certo.

mentor do he-man

Na história de hoje você aprendeu porque apesar de um monte de gente jogar bola todo dia nem todas viram o Ronaldinho (não pude evitar!!!)

 

¹ Essa referência não é minha, li uma vez em algum lugar que não consegui encontrar…não sei se foi em um livro, talvez tenha sido no Lean Startup, já que cito em todos os meus posts, huahuahua

2o Hangout Empreendedorismo Python Brasil

O pessoal do grupo Python Brasil está começando a fazer hangouts semanais pra falar sobre Python e Empreendedorismo e hoje participei do 2o Hangout. Falei um pouco sobre minha experiência e tentei falar alguma coisa sobre a importância do programador também ser um empreendedor.

Tentei falar, porque eu não tinha planejado o que falaria, só rabisquei alguns tópicos no caderno, mas aqui no blog eu posso detalhar um pouco o que eu pensei e colocar uns links também.

Esqueci de citar um dos links principais que gostaria, que é o curso How to Start a Startup do Sam Altman, CEO do YCombinator

Vídeo da Apresentação:

 

Curso

Notícias

Links dos Projetos

Podcasts

Livros

Outros Links

Bônus

Celery com múltiplas filas, retry e tarefas agendadas

No ano passado eu escrevi um post sobre Tarefas demoradas de forma assíncrona com Django e Celery, que serve para a maioria dos casos de uso do celery. O problema é que você pode querer e fazer muito mais com o celery, como ter filas diferentes para tarefas prioritárias, executar um retry após a falha de uma tarefa ou agendar a tarefa para ser executada apenas em outro horário ou outro dia.

Retry em uma Tarefa

Suponhamos que isso seja uma caveira que sua tarefa depende de uma API externa, conexão com algum site ou algum outro motivo que possa levá-la a um ConnectionError, por exemplo.

É provável que dentro de alguns instantes a API, site, etc. esteja no ar e que se você executar sua tarefa novamente ela funcione. Para isso podemos agendar um retry para esta tarefa:


from celery import shared_task
 
@shared_task(bind=True, max_retries=3)
def acessar_sistema_horrivel(self, meu_objeto_id):
    from core.models import MeuObjeto
    from requests import ConnectionError
 
    objeto = MeuObjeto.objects.get(pk=meu_objeto_id)
 
    # Se tiver ConnectionError tenta de novo em 180 segundos
    try:
 
        objeto.acessar_sistema_horrivel()
  
    except ConnectionError as exc:
        self.retry(exc=exc, countdown=180)
 
 

Interessante que dentro de uma função você está usando self.retry. Isso é possível graças ao bind=True no decorator do shared_task, que faz com que nossa função acessar_sistema_horrivel seja na verdade um método da classe Task. Isso nos obriga a colocar self como primeiro argumento da função também.

O número máximo de tentativas para executar essa tarefa é o max_retries=3 definido no decorator

ETA – Agendando a tarefa para começar mais tarde

Agora o cenário é o seguinte, seu sistema vai chamar uma tarefa assíncrona, mais quer que ela seja executada apenas daqui a uma hora.

Para isso podemos chamar a task setando a propriedade ETA (estimated time of arrival) e isso significa que sua tarefa só será executada depois daquele horário (não necessariamente para começar aquele horário, isso vai depender se você possui workers disponíveis, para agendar exatamente como no cron você pode usar o CeleryBeat) .

 


from django.utils import timezone

agora = timezone.now() 
 
# Depois é daqui a uma hora 
#(usei o replace pq quis mostrar, mas vc poderia usar o timedelta para somar também)
depois = agora.replace(hour=agora.hour + 1)
 
acessar_sistema_horrivel.apply_async((meu_objeto_id), eta=depois)
 

Colocando mais de uma fila de execução

Quando você executa o celery ele cria uma fila no seu broker (no caso do post anterior, era o RabbitMQ). A fila padrão chama celery. Qual a implicação? Se você possui várias tarefas assíncronas a serem executadas ficam todas na mesma fila, das qual os workers vão pegar as mensagens e executar.

Imagine que temos além da task acessar_sistema_horrivel, uma outra task chamada tarefa_demorada.

Então dado o seguinte cenário:

3 tarefas: uma lenta e uma mais rápida e a acessar_sistema_horrivel
1 fila
4 workers

E imagine que o sistema produziu 10 requisições da tarefa_demorada para serem executadas e em seguida produziu mais 10 da outra tarefa. O que vai acontecer? Seus workers ficarão todos ocupados buscando executando as requisições de tarefa_demorada que entraram primeiro e nenhum estará trabalhando na tarefa mais rápida.

A solução para isso é produzir filas diferenciadas para as tarefas prioritárias.


# CELERY ROUTES
CELERY_ROUTES = {
    'core.tasks.acessar_sistema_horrivel': {'queue': 'fila_horrivel'},
    'core.tasks.tarefa_demorada': {'queue': 'fila_demorada'},
    'core.tasks.tarefa_mais_rapida': {'queue': 'fila_rapida'},
}

Isso é suficiente para que as mensagens para essas tarefas sejam enviadas para essas filas do RabbitMQ. Detalhe, essas filas são criadas de forma permanente no RabbitMQ, mas isso também pode ser configurado.

Agora para rodar os consumidores para essas filas quando formos executar o celery podemos definir com o parâmetro -Q. No caso fica assim:


celery --app=nome_projeto worker -Q fila_horrivel,fila_demorada,fila_rapida --autoscale=10,5

Estamos iniciando os workers para todas essas filas. Neste caso estou colocando também o parâmetro autoscale, indicando para trabalhar com no mínimo 5 workers e caso haja muitas tarefas o celery adiciona mais workers até o máximo de 10. Isso é bom para economizar recursos do seu sistema, caso você tenha horários com mais produção de tarefas.

Como mostramos no outro post, para usar isto sob o supervisord, você pode modificar aquele mesmo script


[program:celery]
command=/home/deploy/.virtualenvs/meu_virtual_env/bin/celery --app=nome_projeto worker -Q fila_horrivel,fila_demorada,fila_rapida --autoscale=10,5
directory=/home/deploy/webapps/projeto_django
user=nobody
autostart=true
autorestart=true
redirect_stderr=true

Em seguida rodar o reread e o update, porque o arquivo de configuração mudou e reiniciar o supervisord para esse arquivo.


sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart gunicorn

Com isso seus workers vão executar tarefas de cada uma das filas, não travando os workers para só um tipo de tarefa. Há diversas outras formas de colocar seus workers para funcionar.

Chamando Tarefas Sequenciais

Outro problema comum, é precisar chamar duas tarefas assíncronas , uma depois da outra. Isso pode acontecer em diversos cenários, ex: o caso de a segunda tarefa receber como parâmetro o resultado da primeira, ou da primeira tarefa salvar algum dado e a segunda tarefa trabalhar aquele dado, etc…

Para isso nós podemos usar o Chain, que é o recomendado ao invés de chamar a segunda tarefa assíncrona com o delay dentro da primeira tarefa.


from celery import chain
from tasks import salvar_dados, trabalhar_dados
 
# Estamos chamando o método s das tasks, que as usa como subtasks de uma task gerada pelo chain.
chain(salvar_dados.s(meu_objeto_id) | trabalhar_dados.s())

A tarefa trabalhar_dados vai utilizar o return ou resultado da tarefa salvar_dados como parâmetro.
O chain em sí está gerando uma task, então para ele também serve chamar o apply_async com um eta definido, por exemplo:


chain(salvar_dados.s(meu_objeto_id) | trabalhar_dados.s()).apply_async(eta=depois)

Ignorando os resultados do ResultBackend

Se você só usa as tasks para executar alguma coisa que não precisa do return da task, você pode ignorar o ResultBackend e ter um ganho de performance.

A maioria dos casos não precisa guardar o resultado do return, então é só uma dica interessante se você já está salvando algo nos models, ou fazendo whatever diretamente dentro da sua task, coloque isso no seu settings.py:


CELERY_IGNORE_RESULT = True

 

Na verdade tem muito mais coisas legais no Celery, que ficarão pra um próximo post.

Fontes

http://docs.celeryproject.org/en/latest/userguide/tasks.html

http://docs.celeryproject.org/en/latest/userguide/optimizing.html#guide-optimizing

https://denibertovic.com/posts/celery-best-practices/

https://news.ycombinator.com/item?id=7909201

http://docs.celeryproject.org/en/latest/userguide/workers.html

http://docs.celeryproject.org/en/latest/userguide/canvas.html

 

Super Bônus

Celery Messaging at Scale at Instagram – Apresentação Pycon 2013

Tarefas demoradas de forma assíncrona com Django e Celery

Estou escrevendo esse post empolgado por uma lightning talk que fiz no GruPy-SP (16/07). Assim aproveito pra explicar um pouco mais o que estou dizendo nos slides.

O problema de tarefas demoradas do lado do servidor

Sempre que o cliente faz uma requisição web (request), o servidor faz um processamento, normalmente pra quem usa o django, ele lê a requisição, trata os dados recebidos, salva ou recupera registros do banco de dados (através dos models), faz algum processamento do que será exibido para o usuário, renderiza isso em um template e manda uma resposta (response) para o cliente.

Dependendo da tarefa que você executa no servidor a resposta pode demorar muito e isso leva à problemas de TimeOut, a experiência do usuário fica comprometida (quanto mais o servidor demorar pra responder maior a chance do usuário sair do seu site) e também pode acontecer da lentidão ser causada por outros, como por exemplo o uso de uma API externa.

Existem diversas tarefas que podem demorar pra ser executadas. Se você tiver que criar um relatório pesado, acionado por um client web. Se de repente você precisar enviar diferentes emails para uma lista ou por exemplo editar um vídeo depois que o usuário faz o upload na sua página.

Caso real

Esse é um problema que me deparei um dia na geração de um relatório. O relatório levava cerca de 20 minutos para ser gerado, o cliente recebia um erro de timeout e obviamente não dá pra esperar 20 minutos pra gerar alguma coisa. Pra driblar isso e deixar a tarefa em background, alguém resolveu usar um comando do sistema operacional. (No linux se você colocar & no final de um comando esse comando roda em background).

django-view-os-system

Note que em seguida o django envia mostra uma mensagem ao usuário de que ele será avisado por e-mail ao final da geração do relatório.
Alguma coisa não parece certa aqui.

Me arrependo imediatamente dessa decisão

Celery – A solução para esses problemas!

O Celery é um gerenciador de tarefas assíncronas. Com ele você pode executar uma fila de tarefas (que ele recebe por meio de mensagens), pode agendar tarefas direto no seu projeto sem precisar do cron e ele ainda tem integração fácil com a maioria dos frameworks python mais utilizados (django, flask, tornado, etc.)

Como o Celery funciona

Essa imagem kibada da internet dá uma visão geral de como fica o fluxo da sua aplicação.

estrutura-celery

  • O User (ou Client ou Producer) é a sua aplicação Django.
  • O AMPQ Broker é um Message Broker. Um programa responsável por manter a fila de mensagens que serão trocadas entre o seu programa e o Celery, geralmente é o RabbitMQ ou o Redis
  • Os workers (ou consumers) que vão executar as tarefas que você quer que sejam assíncronas.
  • O Result Store, que é onde os workers podem ou não salvar os resultados das tarefas que foram executadas.

O Client pode passar uma tarefa, uma lista de tarefas, tarefas periódicas ou o retry de alguma tarefa pra fila do Message Broker. O Message Broker distribui essas tarefas entre os workers e o resultado dessas tarefas pode ser escrito em um Result Store (Memcahed, RDBMS, MongoDB, etc…) que mais tarde pode ser lido pelo Client novamente.

Qual Broker utilizar?

O recomendado pelo Celery é o RabbitMQ.

Instalando e configurando o RabbitMQ

Existem vários exemplos de como utilizar o Redis, eu vou mostrar como usar o RabbitMQ.

Você só precisa:

  1. Instalar o RabbitMQ (Baixar o pacote no site ou adicionar o repo deles no sources.list e instalar com APT-GET)
    sudo apt-get install rabbitmq-server
  2. Criar um usuário, um virtual host e dar permissões para esse usuário no virtualhost:
    
    sudo rabbitmqctl add_user myuser mypassword
    sudo rabbitmqctl add_vhost myvhost
    sudo rabbitmqctl set_permissions -p myvhost myuser ".*" ".*" ".*"
    

Instalando e configurando o Celery

pip install celery

No seu settings.py:

#Celery Config
BROKER_URL = 'amqp://guest:[email protected]:5672//'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'

Na pasta do projeto (a mesma pasta do settings.py) crie um arquivo chamado celery.py:


from __future__ import absolute_import

import os
from celery import Celery
from django.conf import settings

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'nome_do_proj.settings')

app = Celery('nome_do_proj')

# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

Esse autodiscover_tasks permite que seu projeto ache todas as tarefas assíncronas de cada app instalada nele. Nessa mesma pasta do settings.py, você vai modificar o arquivo de pacote __init__.py:


from __future__ import absolute_import

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

Criando as tarefas assíncronas do seu app

Na pasta do seu app crie um arquivo chamado tasks.py:

from __future__ import absolute_import
from celery import shared_task
from relatorios import gerar_relatorio_excel


@shared_task
def gerar_relatorio(data_inicial, data_final, email):
    gerar_relatorio_excel(
        data_inicial = data_inicial,
        data_final = data_final,
        email = email
    )
    return "Relatorio Excel gerado"

Agora é só fazer importar essa função que foi criada usando o método delay que foi adicionado à sua função pelo decorator shared_task.

tabelao assincrono

Rodando o Celery

Agora você precisa rodar o celery. Ele vai rodar os seus workers pra executar suas tarefas de forma assíncrona. Por padrão ele inicia um worker para cada núcleo de processador, mas você pode determinar quantos com -c Numero_de_workers

celery --app=nome_projeto worker --loglevel=INFO

E então você verá essa tela:

celery-rodando

Você pode então testar sua função assíncrona com o shell do django mesmo:

chama funcao

E o que aparece na tela que está mostrando o celery:

aparece no celery

Colocando o Celery em Produção

Para usar o Celery em produção eu recomendo que você use o Supervisor

Para instalar o supervisor:

sudo apt-get install supervisor

Depois crie um arquivo de configuração para o celery e coloque um link simbólico para esse arquivo no /etc/supervisor/conf.d/

[program:celery]
command=/home/deploy/.virtualenvs/meu_virtual_env/bin/celery --app=nome_proj worker --loglevel=INFO
directory=/home/deploy/webapps/projeto_django
user=nobody
autostart=true
autorestart=true
redirect_stderr=true

Para o supervisor ler novamente os arquivos que estão em sua pasta de configuração:

sudo supervisorctl reread
sudo supervisorctl update

Agora é só iniciar o celery:

sudo supervisorctl start celery

Bom, acho que é só isso. Esses são os slides da minha apresentação:

Continuação

Celery com múltiplas filas, retry e tarefas agendadas

Fontes

E aqui vão as fontes de pesquisa (ou algumas) que eu utilizei nesse processo:

Bônus:

Como o Feed do Instagram usa o Celery e o RabbitMQ

*args e **kwargs não são coisas mágicas em Python

* é um operador e não é só de multiplicação. Hun!?

Muita gente acha que *args e **kwargs são coisas mágicas em Python.

* é um operador que transforma listas em argumentos de uma função.

Por exemplo, imagina que você quer usar a função replace em uma string. Essa função recebe 2 argumentos, o primeiro para o valor que precisa ser substituído e o segundo para o novo valor.

>>> 'a escambau'.replace('a ', 'o ')
>>> 'o escambau'
>>> # Agora tentando passar os parâmetros com uma lista
>>>
>>> args = ['a ','o ']
>>> 'a escambau'.replace(args)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: replace() takes at least 2 arguments (1 given)
>>> 
>>> #Para que isso funcione basta usar o operador *
>>> 'a escambau'.replace(*args)
'o escambau'


Super simples.

O ** segue a mesma lógica, é um operador que transforma um dicionário em keyword arguments de uma função.

Vou criar uma função que recebe dois argumentos e printa o nome com a idade (como na revista caras)

>>> d = {'nome' : 'Fernando', 'idade': 29}
>>> def caras_me(nome,idade):
...     print("%s(%s)" % (nome,idade))
... 
>>> #Colocando só o dicionário sem o operador:
>>> caras_me(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: caras_me() takes exactly 2 arguments (1 given)
>>> # Colocando o dicionário usando o operador **
>>> caras_me(**d)
Fernando(29)

Você pode dar uma olhada na documentação oficial, caso queira:

https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument-lists

Usando Regex para fazer split para mais de um valor

Precisei fazer split em alguns textos jurídicos e como na língua portuguesa os adjetivos sofrem flexão de gênero , tive que separar um texto por mais de um valor.

A sorte é que o módulo re de regex do python é excelente e vem com essa função split, você só precisa passar o pattern e o texto. No caso eu usei ‘Advogado|Advogada ‘, lembrando que o |(pipe) é um “ou” em expressões regulares.

Como criar um instalador MSI utilizando o Inno Setup

Essa é a terceira parte da série de Posts Distribuir Programas em Python para Desktops Windows. Nela você vai aprender como criar um instalador MSI utilizando o Inno Setup e colocando as DLLs do MSVCR para o Python funcionar em qualquer máquina windows.

As outras duas partes são:

Bom, nós vimos como criar um instalador MSI com o cx_freeze e utilizando as DLL’s de MSVCR da própria página no primeiro post, depois vimos como modificar o programa para ele ter um Auto-Update. Só que quando alteramos o setup.py pra gerar o programa com o Auto-Update não conseguimos mais fazer um instalador com o cx_freeze. Isso acontece porque o Esky modifica o seu programa, ele cria um executável que verifica se há algum download disponível no FTP, se tiver ele faz o download, faz o update, verifica se deu certo e exclui as bibliotecas antigas.

Primeira coisa: baixar e instalar o Inno Setup.

O Inno Setup cria um arquivo .iss que nada mais é que um script para criar o seu instalador. Abrindo o Inno você tem a opção de escrever esse script diretamente ou usar o Script Wizard (Um assistente):

inno

 

Nós vamos criar um instalador usando o assistente, primeiro nós vamos utilizar aquele arquivo que geramos no post anterior, o boneca-1.0.1.win32.zip. Você precisa ter descompactado esse arquivo:

boneca-esky-conteudo

No Inno Setup clique em File >> New. Você abrirá o Assistente, ele é fácil de usar, preencha esses dados como quiser:

assistente-inno-setup-1

Na próxima tela você pode escolher onde o Instalador vai colocar o seu programa. O Default é o Program Files folder, mas tem um porém: Se o seu código não tiver Assinatura Digital (Code Signing) você vai ter dificuldades com o UAC do Windows, porque ele não vai conferir a autenticidade do seu código e isso implicará em problemas com antivírus, em bloqueio do programa pelo windows e principalmente ele pode impedir que o programa faça o auto-update, por isso use uma outra pasta a princípio. Você pode escrever um caminho ou usar uma Constante das pastas do Windows do próprio Inno Setup. [Recomendável]

assistente-inno-setup-2

Na terceira tela você vai colocar quais os programas, pastas e arquivos serão instalados pelo instalador, no caso o boneca.exe e o python27.dll ficaram na raiz e também a pasta boneca-1.0.1.win32 com o conteúdo dela.

Então coloque boneca.exe no campo de Application main executable file
Em seguida, clique em Add File para colocar o python27.dll e depois em Add Folder para adicionar a pasta boneca-1.0.1.win32:

assistente-inno-setup-3

Agora é só seguir em diante no assistente, no final ele vai criar o arquivo .iss. Você já pode compilar o arquivo e ele vai gerar o instalador .msi mas está faltando agora adicionar as DLLs do MSVCR. Então baixe de acordo com a sua versão do python:

Agora você precisa modificar o código do seu arquivo .iss pra ele instalar as DLLs junto com o seu programa. Isso aqui eu usei essa solução que achei no StackOverFlow e ela funciona muito bem:

Na seção Files coloque o caminho do vc_redist que você baixou:

[Files]
Source: "vcredist_x86.exe"; DestDir: {tmp}; Flags: deleteafterinstall

No final da seção Run adicione esse código:

[Run]
; add the Parameters, WorkingDir and StatusMsg as you wish, just keep here
; the conditional installation Check
Filename: "{tmp}\vcredist_x86.exe"; Check: VCRedistNeedsInstall

[Code]
#IFDEF UNICODE
 #DEFINE AW "W"
#ELSE
 #DEFINE AW "A"
#ENDIF
type
 INSTALLSTATE = Longint;
const
 INSTALLSTATE_INVALIDARG = -2; // An invalid parameter was passed to the function.
 INSTALLSTATE_UNKNOWN = -1; // The product is neither advertised or installed.
 INSTALLSTATE_ADVERTISED = 1; // The product is advertised but not installed.
 INSTALLSTATE_ABSENT = 2; // The product is installed for a different user.
 INSTALLSTATE_DEFAULT = 5; // The product is installed for the current user.

 VC_2005_REDIST_X86 = '{A49F249F-0C91-497F-86DF-B2585E8E76B7}';
 VC_2005_REDIST_X64 = '{6E8E85E8-CE4B-4FF5-91F7-04999C9FAE6A}';
 VC_2005_REDIST_IA64 = '{03ED71EA-F531-4927-AABD-1C31BCE8E187}';
 VC_2005_SP1_REDIST_X86 = '{7299052B-02A4-4627-81F2-1818DA5D550D}';
 VC_2005_SP1_REDIST_X64 = '{071C9B48-7C32-4621-A0AC-3F809523288F}';
 VC_2005_SP1_REDIST_IA64 = '{0F8FB34E-675E-42ED-850B-29D98C2ECE08}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_X86 = '{837B34E3-7C30-493C-8F6A-2B0F04E2912C}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_X64 = '{6CE5BAE9-D3CA-4B99-891A-1DC6C118A5FC}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_IA64 = '{85025851-A784-46D8-950D-05CB3CA43A13}';

 VC_2008_REDIST_X86 = '{FF66E9F6-83E7-3A3E-AF14-8DE9A809A6A4}';
 VC_2008_REDIST_X64 = '{350AA351-21FA-3270-8B7A-835434E766AD}';
 VC_2008_REDIST_IA64 = '{2B547B43-DB50-3139-9EBE-37D419E0F5FA}';
 VC_2008_SP1_REDIST_X86 = '{9A25302D-30C0-39D9-BD6F-21E6EC160475}';
 VC_2008_SP1_REDIST_X64 = '{8220EEFE-38CD-377E-8595-13398D740ACE}';
 VC_2008_SP1_REDIST_IA64 = '{5827ECE1-AEB0-328E-B813-6FC68622C1F9}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_X86 = '{1F1C2DFC-2D24-3E06-BCB8-725134ADF989}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_X64 = '{4B6C7001-C7D6-3710-913E-5BC23FCE91E6}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_IA64 = '{977AD349-C2A8-39DD-9273-285C08987C7B}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_X86 = '{9BE518E6-ECC6-35A9-88E4-87755C07200F}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_X64 = '{5FCE6D76-F5DC-37AB-B2B8-22AB8CEDB1D4}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_IA64 = '{515643D1-4E9E-342F-A75A-D1F16448DC04}';

 VC_2010_REDIST_X86 = '{196BB40D-1578-3D01-B289-BEFC77A11A1E}';
 VC_2010_REDIST_X64 = '{DA5E371C-6333-3D8A-93A4-6FD5B20BCC6E}';
 VC_2010_REDIST_IA64 = '{C1A35166-4301-38E9-BA67-02823AD72A1B}';
 VC_2010_SP1_REDIST_X86 = '{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}';
 VC_2010_SP1_REDIST_X64 = '{1D8E6291-B0D5-35EC-8441-6616F567A0F7}';
 VC_2010_SP1_REDIST_IA64 = '{88C73C1C-2DE5-3B01-AFB8-B46EF4AB41CD}';

 // Microsoft Visual C++ 2012 x86 Minimum Runtime - 11.0.61030.0 (Update 4) 
 VC_2012_REDIST_MIN_UPD4_X86 = '{BD95A8CD-1D9F-35AD-981A-3E7925026EBB}';
 VC_2012_REDIST_MIN_UPD4_X64 = '{CF2BEA3C-26EA-32F8-AA9B-331F7E34BA97}';
 // Microsoft Visual C++ 2012 x86 Additional Runtime - 11.0.61030.0 (Update 4) 
 VC_2012_REDIST_ADD_UPD4_X86 = '{B175520C-86A2-35A7-8619-86DC379688B9}';
 VC_2012_REDIST_ADD_UPD4_X64 = '{37B8F9C7-03FB-3253-8781-2517C99D7C00}';

function MsiQueryProductState(szProduct: string): INSTALLSTATE; 
 external 'MsiQueryProductState{#AW}@msi.dll stdcall';

function VCVersionInstalled(const ProductID: string): Boolean;
begin
 Result := MsiQueryProductState(ProductID) = INSTALLSTATE_DEFAULT;
end;

function VCRedistNeedsInstall: Boolean;
begin
 // here the Result must be True when you need to install your VCRedist
 // or False when you don't need to, so now it's upon you how you build
 // this statement, the following won't install your VC redist only when
 // the Visual C++ 2010 Redist (x86) and Visual C++ 2010 SP1 Redist(x86)
 // are installed for the current user
 Result := not (VCVersionInstalled(VC_2010_REDIST_X86) and 
 VCVersionInstalled(VC_2010_SP1_REDIST_X86));
end;


Agora salve e compile seu arquivo. Você terá um arquivo setup.exe como saída na pasta Output (ou o nome que você colocou) e ele vai instalar o seu programa da boneca com as DLLs necessárias pra rodar em qualquer Windows.

Conclusão:

Se você acompanhou os 3 posts você conseguiu criar um programa em python com auto-update e distribuí-lo para qualquer computador windows.

Observações Final sobre Code Signing:

Fiz uma pesquisa sobre code signing no ano passado(2014) , pode ser interessante para alguém:

Autoridade Certificadora Link Preço / ano
Global Sign https://www.globalsign.com/code-signing/ 229 usd
Thawte http://www.thawte.com/code-signing/index.html?tid=a_box_buycs 299 usd
Symantec Verisign http://www.symantec.com/code-signing 499 usd
Godaddy Code Signing http://br.godaddy.com/ssl/code-signing-certificate.aspx 479,99 reais / 199.99 usd
Ksoftware (Comodo partner) http://codesigning.ksoftware.net/ 95 usd
Digicert http://www.digicert.com/code-signing/ev-code-signing-compared.htm 223 usd
Obs:
Comparativo interessante: https://www.sslshopper.com/cheap-code-signing-certificates.html