Planeta PythonBrasil

PythonBrasil[10]

June 05, 2018

Vinta Software

Don't forget the stamps: testing email content in Django

When developing a web app how often do you check the emails you send are all working properly? Not as often as your web pages, right? That's ok, don't feel guilty, emails are hard to test and they are often someone's else responsibility to write and take care. This doesn't mean we should give up on them. There are some things we can do to prevent e

05 de June de 2018 às 12:49

How I test my DRF serializers

How I test my DRF serializers In this blog post, I will show the whats and whys on testing Django REST Framework serializers. First, some context. Here is the model setup we are going to use for this example: from django.db import models class Bike(models.Model): COLOR_OPTIONS = (('yellow', 'Yellow'), ('red', 'Red&#

05 de June de 2018 às 12:49

Metaprogramming and Django - Using Decorators

While programming is about, in some way, doing code to transform data, metaprogramming can be seen as the task of doing code to change code. This category is often used to help programmers to enhance the readability and maintainability of the code, help with separation of concerns and respect one of the most important principles of software develop

05 de June de 2018 às 12:49

Vinta's Review of PythonBrasil[12]

PythonBrasil[12] happened in Florianópolis - SC and lasted for 6 days. We saw some amazing Keynotes from some awesome speakers, such as @SagnewShreds, @hannelita, @NaomiCeder, @freakboy3742, @seocam we got to have a lot of community time getting to know new people from all around Brazil and still got to present 4 talks(Hooray!!). On the following

05 de June de 2018 às 12:49

Python API clients with Tapioca

In this post I'll present to you Tapioca, a Python library to create powerful API clients with very few lines of code. If you don't want to read through the reasons why I've built it, you may just jump straight to the Tapioca Wrapper section. Why do we need a better way to build API clients Integrating with external services is painful. Here at Vin

05 de June de 2018 às 12:49

[pt-BR] PythonBrasil[12] Talks

Slides from talks given during the PythonBrasil[12] event will be posted here. This post and the slides are in written in Brazilian Portuguese. O que é esse tal de REST? Palestrante: @xima Link dos slides: O que é esse tal de REST? REST é a bola vez quando falamos sobre API. As maioria dos serviços que encontramos na web fornece interfaces dest

05 de June de 2018 às 12:49

Database concurrency in Django the right way

When developing applications which have real-time requirements or other specific needs for running asynchronous tasks outside the web application, it is common to adopt a task queue such as Celery. This allows, for example, for the server to handle a request, start an asynchronous task responsible of doing some heavyweight processing, and return an answer while the task is still running. Here, we are considering a similar scenario: a request is made, and the server has to do some processing on the request. Ideally, we want to separate the high time-demanding parts from the view processing flow, so we run those parts in a separate task. Now, let's suppose we have to do some database operations both in the view and the task when the request happens. If not done carefully, those operations can be a source for issues that can be hard to track.

05 de June de 2018 às 12:49

Understanding Time Series Forecasting with Python

Vinta is a software studio whose focus is to produce high quality software and give clients great consulting advices to make their businesses grow. However, even though our main focus is web development, we also do our share of machine learning over here. This article is the first of a few designed to show everything (or almost everything) you need

05 de June de 2018 às 12:49

Taming Irreversibility with Feature Flags (in Python)

Feature Flags are a very simple technique to make features of your application quickly toggleable. The way it works is, everytime we change some behavior in our software, a logical branch is created and this new behavior is only accessible if some specific configuration variable is set or, in certain cases, if the application context respects some

05 de June de 2018 às 12:49

Dealing with resource-consuming tasks on Celery

In this post, we will talk about how you can optimize your Celery tasks and avoid certain kind of problems related to resource-consuming tasks. If you are new to Celery or want to know more about it before reading this, you might wanna check first this post with an overview about Celery and this one with some more advanced tips and tricks. When w

05 de June de 2018 às 12:49

Django REST Framework Read & Write Serializers

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

05 de June de 2018 às 12:49

Celery in the wild: tips and tricks to run async tasks in the real world

This post is aimed at people with some experience writing async taks, if you are starting on Celery you might want to read this other post I wrote before starting on this one. The thing about async tasks is that the hard part is not how to run them [although it can be fairly complicated to understand the architecture and set up things when you a

05 de June de 2018 às 12:49

Celery: an overview of the architecture and how it works

Asynchronous task queues are tools to allow pieces of a software program to run in a separate machine/process. It is often used in web architectures as way to delegate long lasting tasks while quickly answering requests. The delegated task can trigger an action such as sending an email to the user or simply update data internally in the system when

05 de June de 2018 às 12:49

[pt-BR] PythonBrasil[13] Talks

Vinhemos para a Python Brasil novamente. Dessa vez aprovamos 6 palestras. Indo desde Python para uso científico até técnicas para salvar grandes projetos.

05 de June de 2018 às 12:49

Multitenancy: juggling customer data in Django

Suppose you want to build a new SaaS (Software as a Service) application. Suppose your application will store sensitive data from your customers. What is the best way to guarantee the isolation of the data and make sure information from one client does not leak to the other? The answer to that is: it depends. It depends on the number of customers y

05 de June de 2018 às 12:49

Vinta's Talks Around the Globe: DjangoConUS, PyBay2017 and DjangoConAU

Slides from talks given during the DjangoConUS, PyBay2017 and DjangoConAU events will be posted here

05 de June de 2018 às 12:49

PyCon US 2017: the biggest Python Event in the World

Pycon 2017 happened in Oregon, Portland! If you wanted to discuss anything about Python, that was the place to be. It was the biggest Python event of the world, it lasted from May 17th to May 25th. I got to see talks from some important names on it, like Lisa Guo and Katy Huff, both of them are using Python to make great things! Lisa is using on In

05 de June de 2018 às 12:49

[pt-BR] Python Nordeste 2017 Talks

Slides from talks given during the Python Nordeste 2017 event will be posted here. This post and the slides are written in Brazilian Portuguese. 5 meses de Python: o que aprendi Palestrante: @rsarai Link dos slides: 5 meses de Python: o que aprendi Trabalhar como desenvolvedor de software pode ser um pouco frustrante, as vezes por estar preso a

05 de June de 2018 às 12:49

[Talk] All Things Python meetup in Sunnyvale

I'll be talking at the All Things Python meetup! It will happen on June 6 in Sunnyvale, California. I'll be talking about good practices designing async tasks and some advanced Celery features.This will be a first version of the talk I'm preparing for DjangoCon US in August. For signing up or more information, this is the link to the event. Looki

05 de June de 2018 às 12:49

Advanced Django querying: sorting events by date

Imagine the situation where our application has events (scheduled tasks, appointments, python conferences across the world) happening in different moments of time. Almost anything with a date attached to it. We want to display them in a simple list to the user. Given we are in February 2017 (the date this post was written), what would be the best w

05 de June de 2018 às 12:49

Contributing to Django Framework is easier than you think

For those who are starting to code and wish to make open source, sometimes it is hard to start. The idea of contributing with that fancy and wonderful lib that you love can sound a little bit scary. Lucky for us many of those libs have room for whoever is willing to start. They also give us the support that we need. Pretty sweet, right? Do you know

05 de June de 2018 às 12:49

May 31, 2018

Bruno Cezar Rocha

Dynaconf 1.0.x released - Layered configuration system for python with flask and django support

Dynaconf

dynaconf - The dynamic configurator for your Python Project

MIT License PyPI PyPI Travis CI codecov Codacy grade

dynaconf a layered configuration system for Python applications - with strong support for 12-factor applications and extensions for Flask and Django.

Features

  • Strict separation of settings from code (following 12-factor applications Guide).
  • Define comprehensive default values.
  • Store parameters in multiple file formats (.toml, .json, .yaml, .ini and .py).
  • Sensitive secrets like tokens and passwords can be stored in safe places like .secrets file or vault server.
  • Parameters can optionally be stored in external services like Redis server.
  • Simple feature flag system.
  • Layered [environment] system.
  • Environment variables can be used to override parameters.
  • Support for .env files to automate the export of environment variables.
  • Correct data types (even for environment variables).
  • Have only one canonical settings module to rule all your instances.
  • Drop in extension for Flask app.config object.
  • Drop in extension for Django conf.settings object.
  • Powerful $ dynaconf CLI to help you manage your settings via console.
  • Customizable Validation System to ensure correct config parameters.
  • Allow the change of dynamic parameters on the fly without the need to redeploy your application.
  • Easily extensible
  • 100% test coverage
  • 100% documented

Read the Full Documentation at: http://dynaconf.readthedocs.io/

Repository: http://github.com/rochacbruno/dynaconf/

Getting Started

Installation

Python 3.x is required

$ pip install dynaconf

Default installation supports .toml, .py and .json file formats and also environment variables (.env supported)

Usage

Accessing config variables in your Python application

In your Python program wherever you need to access a settings variable you use the canonical object from dynaconf import settings:

NOTE: Read the full documentation for more examples like using Dynaconf with Flask or Django

Example of program to connect to some database

from some.db import Client

from dynaconf import settings               # The only object you need to import

conn = Client(
    username=settings.USERNAME,             # attribute style access
    password=settings.get('PASSWORD'),      # dict get style access
    port=settings['PORT'],                  # dict item style access
    timeout=settings.as_int('TIMEOUT'),     # Forcing casting if needed
    host=settings.get('HOST', 'localhost')  # Providing defaults
)

Where the settings values are stored

Dynaconf aims to have a flexible and usable configuration system. Your applications can be configured via a configuration files, through environment variables, or both. Configurations are separated into environments: [development], [staging], [testing] and [production]. The working environment is selected via an environment variable.

Sensitive data like tokens, secret keys and password can be stored in .secrets.* files and/or external storages like Redis or vault secrets server.

Besides the built-in optional support to redis as settings storage dynaconf allows you to create custom loaders and store the data wherever you want e.g: databases, memory storages, other file formats, nosql databases etc.

Working environments

At any point in time, your application is operating in a given configuration environment. By default there are four such environments:

  • [development]
  • [staging]
  • [testing]
  • [production]

You can also define [custom environment] and use the pseudo-envs [default] to provide comprehensive default values and [global] to provide global values to overrride in any other environment.

Without any action, your applications by default run in the [development] environment. The environment can be changed via the ÈNV_FOR_DYNACONF environment variable. For example, to launch an application in the [staging] environment, we can run:

export ENV_FOR_DYNACONF=staging

or

ENV_FOR_DYNACONF=staging python yourapp.py

NOTE: When using FLask Extension the environment can be changed via FLASK_ENV variable and for Django Extension you can use DJANGO_ENV.

The settings files

NOTE: Read the full documentaion about dynaconf CLI to learn how to automatically create the settings files for your project.

An optional settings.{toml|py|json|ini|yaml} file can be used to specify the configuration parameters for each environment. If it is not present, only the values from environment variables are used (.env file is also supported). Dynaconf searches for the file starting at the current working directory. If it is not found there, Dynaconf checks the parent directory. Dynaconf continues checking parent directories until the root is reached.

The recommended file format is TOML but you can choose to use any of .{toml|py|json|ini|yaml}.

The file must be a series of sections, at most one for [default], optionally one for each [environment], and an optional [global] section. Each section contains key-value pairs corresponding to configuration parameters for that [environment]. If a configuration parameter is missing, the value from [default] is used. The following is a complete settings.toml file, where every standard configuration parameter is specified within the [default] section:

NOTE: if the file format choosen is .py as it does not support sections you can create multiple files like settings.py for [default], development_settings.py, production_settings.py and global_settings.py. ATTENTION using .py is not recommended for configuration use TOML!

[default]
username = "admin"
port = 5000
host = "localhost"
message = "default message"
value = "default value"

[development]
username = "devuser"

[staging]
host = "staging.server.com"

[testing]
host = "testing.server.com"

[production]
host = "server.com"

[awesomeenv]
value = "this value is set for custom [awesomeenv]"

[global]
message = "This value overrides message of default and other envs"

The [global] pseudo-environment can be used to set and/or override configuration parameters globally. A parameter defined in a [global] section sets, or overrides if already present, that parameter in every environment. For example, given the following settings.toml file, the value of address will be "1.2.3.4" in every environment:

[global]
address = "1.2.3.4"

[development]
address = "localhost"

[production]
address = "0.0.0.0"

NOTE: The [env] name and first level variables are case insensitive as internally dynaconf will always use upper case, that means [development] and [DEVELOPMENT] are equivalent and address and ADDRESS are also equivalent. This rule does not apply for inner data structures as dictionaries and arrays.

Supported file formats

By default toml is the recommended format to store your configuration, however you can switch to a different supported format.

# If you wish to include support for more sources
pip3 install dynaconf[yaml|ini|redis|vault]

# for a complete installation
pip3 install dynaconf[all]

Once the support is installed no extra configuration is needed to load data from those files, dynaconf will search for settings files in the root directory of your application looking for the following files in the exact order below:

DYNACONF_LOADING_ORDER = [
 'settings.py',
 '.secrets.py',
 'settings.toml',
 '.secrets.toml',
 'settings.yaml',
 '.secrets.yaml',
 'settings.ini',
 '.secrets.ini',
 'settings.json',
 '.secrets.json',
 # redis server if REDIS_ENABLED_FOR_DYNACONF=true
 # vault server if VAULT_ENABLED_FOR_DYNACONF=true
 # other sources if custom loaders are defined
 # All environment variables prefixed with DYNACONF_
]

NOTE: Dynaconf works in an layered override mode based on the above order, so if you have multiple file formats with conflicting keys defined, the precedence will be based on the loading order.

Take a look at the example folder to see some examples of use with different file formats.

Sensitive secrets

Using .secrets files

To safely store sensitive data Dynaconf also searches for a .secrets.{toml|py|json|ini|yaml} file to look for data like tokens and passwords.

example .secrets.toml:

[default]
password = "sek@987342$"

The secrets file supports all the environment definitions supported in the settings file.

IMPORTANT: The reason to use a .secrets.* file is the ability to omit this file when commiting to the repository so a recommended .gitignore should include .secrets.* line.

Using Vault server

The vaultproject.io/ is a key:value store for secrets and Dynaconf can load variables from a Vault secret.

  1. Run a vault server

Run a Vault server installed or via docker:

$ docker run -d -e 'VAULT_DEV_ROOT_TOKEN_ID=myroot' -p 8200:8200 vault
  1. Install support for vault in dynaconf
$ pip install dynaconf[vault]
  1. In your .env file or in exported environment variables define:
VAULT_ENABLED_FOR_DYNACONF=true
VAULT_URL_FOR_DYNACONF="http://localhost:8200"
VAULT_TOKEN_FOR_DYNACONF="myroot"

Now you can have keys like PASSWORD and TOKEN defined in the vault and dynaconf will read it.

To write a new secret you can use http://localhost:8200 web admin and write keys under the /secret/dynaconf secret database.

You can also use the Dynaconf writer via console

$ dynaconf write vault -s password=123456

Environment variables

overloading parameters via env vars

All configuration parameters, including custom environments and dynaconf configuration, can be overridden through environment variables.

To override the configuration parameter {param}, use an environment variable named DYNACONF_{PARAM}. For instance, to override the "HOST" configuration parameter, you can run your application with:

DYNACONF_HOST='otherhost.com' python yourapp.py

.env files

If you don't want to declare the variables on every program call you can run export DYNACONF_{PARAM} in your shell or put the values in a .env file located in the same directory as your settings files (the root directory of your application), variables in .env does not overrride existing environment variables.

IMPORTANT: Dynaconf will search for a .env located in the root directory of your application, if not found it will continue searching in parent directories until it reaches the root. To avoid conflicts we recommend to have a .env even if it is empty.

Precedence and type casting

Environment variables take precedence over all other configuration sources: if the variable is set, it will be used as the value for the parameter even if parameter exists in settings files or in .env.

Variable values are parsed as if they were TOML syntax. As illustration, consider the following examples:

# Numbers
DYNACONF_INTEGER=42
DYNACONF_FLOAT=3.14

# Text
DYNACONF_STRING=Hello
DYNACONF_STRING="Hello"

# Booleans
DYNACONF_BOOL=true
DYNACONF_BOOL=false

# Use extra quotes to force a string from other type
DYNACONF_STRING="'42'"
DYNACONF_STRING="'true'"

# Arrays must be homogenous in toml syntax
DYNACONF_ARRAY=[1, 2, 3]
DYNACONF_ARRAY=[1.1, 2.2, 3.3]
DYNACONF_ARRAY=['a', 'b', 'c']

# Dictionaries
DYNACONF_DICT={key="abc",val=123}

# toml syntax does not allow `None/null` values so use @none
DYNACONF_NONE='@none None'

# toml syntax does not allow mixed type arrays so use @json
DYNACONF_ARRAY='@json [42, 3.14, "hello", true, ["otherarray"], {"foo": "bar"}]'

NOTE: Older versions of Dynaconf used the @casting prefixes for env vars like export DYNACONF_INTEGER='@int 123' still works but this casting is deprecated in favor of using TOML syntax described above. To disable the @casting do export AUTO_CAST_FOR_DYNACONF=false

The global prefix

The DYNACONF_{param} prefix is set by GLOBAL_ENV_FOR_DYNACONF and serves only to be used in environment variables to override config values.

This prefix itself can be changed to something more significant for your application, however we recommend kepping DYNACONF_{param} as your global env prefix.

NOTE: See the Configuring dynaconf section in documentation to learn more on how to use .env variables to configure dynaconf behavior.

Flask Extension

Dynaconf provides a drop in replacement for app.config.

As Flask encourages the composition by overriding the config_class attribute this extension follows the patterns of Flask and turns your Flask's app.config in to a dynaconf instance.

Initialize the extension

Initialize the FlaskDynaconf extension in your app

from flask import Flask
from dynaconf import FlaskDynaconf

app = Flask(__name__)
FlaskDynaconf(app)

You can optionally use init_app as well.

Use FLASK_ environment variables

Then the app.config will work as a dynaconf.settings instance and FLASK_ will be the global prefix for exporting environment variables.

Example:

export FLASK_DEBUG=true              # app.config.DEBUG
export FLASK_INTVALUE=1              # app.config['INTVALUE']
export FLASK_MAIL_SERVER='host.com'  # app.config.get('MAIL_SERVER')

Settings files

You can also have settings files for your Flask app, in the root directory (the same where you execute flask run) put your settings.toml and .secrets.toml files and then define your environments [default], [development] and [production].

To switch the working environment the FLASK_ENV variable can be used, so FLASK_ENV=development to work in development mode or FLASK_ENV=production to switch to production.

IMPORTANT: To use $ dynaconf CLI the FLASK_APP must be defined.

IF you don't want to manually create your config files take a look at the CLI

Django Extension

Dynaconf a drop in replacement to django.conf.settings.

Following this pattern recommended pattern this extension makes your Django's conf.settings in to a dynaconf instance.

Initialize the extension

In your django project's settings.py include:

INSTALLED_APPS = [
    'dynaconf.contrib.django_dynaconf',
    ...
]

NOTE: The extension must be included as the first INSTALLED_APP of the list

Use DJANGO_ environment variables

Then django.conf.settings will work as a dynaconf.settings instance and DJANGO_ will be the global prefix to export environment variables.

Example:

export DJANGO_DEBUG=true     # django.conf.settings.DEBUG
export DJANGO_INTVALUE=1     # django.conf.settings['INTVALUE]
export DJANGO_HELLO="Hello"  # django.conf.settings.get('HELLO)

Settings files

You can also have settings files for your Django app, in the root directory (the same where manage.py is located) put your settings.toml and .secrets.toml files and then define your environments [default], [development] and [production].

To switch the working environment the DJANGO_ENV variable can be used, so DJANGO_ENV=development to work in development mode or DJANGO_ENV=production to switch to production.

IMPORTANT: To use $ dynaconf CLI the DJANGO_SETTINGS_MODULE must be defined.

IF you don't want to manually create your config files take a look at the CLI

NOTE: It is recommended that all the django's internal config vars should be kept in the settings.py of your project, then application specific values you can place in dynaconf's settings.toml in the root (same folder as manage.py). You can override settings.py values in the dynaconf settings file as well.

How to contribute

In github repository issues and Pull Request are welcomed!

  • New implementations
  • Bug Fixes
  • Bug reports
  • More examples of use in /example folder
  • Documentation
  • Feedback as issues and comments ot joining dynaconf on Telegram or #dynaconf on freenode
  • Donation to rochacbruno [at] gmail.com in PayPal

Read the docs

Documentation: http://dynaconf.readthedocs.io/

Repository: http://github.com/rochacbruno/dynaconf/

por Bruno Rocha em 31 de May de 2018 às 15:23

May 26, 2018

Humberto Rocha

1, 2, 3 Python! O Terminal

Começar a programar é um processo que lembra muito uma montanha russa, cheio de pontos altos e baixos, momentos de emoção e adrenalina, mas também de medo e inseguranças.

E, assim como em uma montanha russa, muitas vezes sua primeira experiência pode determinar se vamos nos aventuraremos em mais experiências como essas ou se iremos sair correndo sem nunca mais entrar em uma montanha russa novamente.

Pensando nisso, e lembrando um pouco de minhas primeiras experiências com programação que estou começando esta série de postagens com o foco em quem está começando a se aventurar no mundo da programação python.

imagem montanha russa

O carrinho

Como todo passeio de montanha russa, precisamos de um carrinho para nos levar pelos trilhos. E é com grande emoção que eu apresento a vocês o nosso carrinho:

imagem terminal

Isso mesmo, o bom e velho terminal. Eu sei que para muitas pessoas o medo já começa aqui, e que muita gente também tem preferência por IDEs que resolvam tudo onde basta clicar no play e está tudo rodando, mas tem uma coisa importante que precisamos ter em mente "é o conhecimento do truque que separa o mago da plateia".

Em algum momento você pode esbarrar em alguma limitação da IDE, decidir usar outra ferramenta ou simplesmente não estar em sua máquina. E a falta de intimidade com o terminal pode gerar uma barreira de aprendizagem e te atrapalhar em evoluir na programação.

Então vamos nos sentir confortáveis com nosso carrinho e checar as "travas de segurança" antes de partir. Nesta primeira postagem em especial pode ser um ponto de partida para outras linguagens além de python.

O terminal

O terminal é uma interface de controle do seu sistema operacional que funciona através de texto. Toda operação feita através de cliques na interface gráfica pode ser reproduzida através dele. Algumas dão mais trabalho outras menos, mas algo que é muito comum de acontecer quando se acostuma com o terminal é preferi-lo ao ter que aprender os comandos de cada interface gráfica.

Os terminais em sua maioria possuem comandos iguais ou muito similares por seguir o padrão POSIX e seus sistemas operacionais terem um "parente" em comum lá no passado, o Unix.

Eu recomendo fortemente para quem está começando e quer aprender a usar o terminal a seguir a Julia Evans @b0rk no twitter, ela faz um trabalho maravilhoso postando resumos de comandos do terminal como este:

imagem do comando man

Ela também produz fanzines sobre sistemas operacionais e ferramentas de debug, vale muito a pena conferir: https://jvns.ca/zines/.

Infelizmente nem tudo são flores, os terminais CMD e PowerShell do Windows são ponto fora da curva, pois seguem uma arquiteturas e padrões bem distintos dos demais. Mas nem tudo está perdido, existem terminais compatíveis com o padrão POSIX como o Cygwin e agora, a partir do Windows 10 é possível rodar terminais linux como o terminal do Ubuntu dentro do Windows \o/.

Outro aspecto que pode causar confusão é se o terminal do seu sistema operacional é case-sensitive, ou seja, se ele diferencia maiúsculo de minúsculo, e como ele trata espaçamento e acentuação em nomes de pastas e arquivos. O python possuí uma biblioteca padrão que lida muito bem com estas diferenças , mas, eu pessoalmente dou preferência em nomear meus arquivos e pastas que serão usados em meus programas somente com caracteres minúsculos substituindo espaço por underline "_". Isso me poupa bastante de ter que lidar com problemas inesperados, de algo que deixou de funcionar em algum sistema operacional por descuido na hora de tratar casos especiais de acesso a arquivos.

E por último o que muda entre os terminais dos sistemas operacionais é o gerenciador de pacotes, que é o responsável pela instalação dos programas em sua máquina. No Mac o mais usado é o brew, no windows tem o chocolatey e no Linux varia de distribuição para distribuição (apt, yum, pacman e etc). Mas neste caso é mais tranquilo pois as documentações destas ferramentas são bem completas e dicas de como instalar programas em cada uma delas existem em grande volume na internet.

Concluindo

Cintos apertados? Confortável com o seu carrinho? Estar pronto para utilizar o terminal quando for preciso tornará sua viagem pela montanha russa da programação python muito mais divertida.

Na próxima postagem vou cobrir o processo de instalação do python nos principais sistemas operacionais: Linux, Windows e Mac. Provavelmente não conseguirei 100% dos casos de instalação de primeira, então conto com vocês para me ajudar a testar e apontar algo que não funcione para que eu possa atualizar a postagem :)

26 de May de 2018 às 00:00

May 21, 2018

Thiago Avelino

Why is the name of the language, "Clojure"?

Many who did not seek the “history” thinks:

Did you pick the name based on starting with the word "closure" and replacing the "s" with "j" for Java? It seems pretty likely, but it would be nice to have that confirmed.

Continue reading at avelino.xxx.

por Avelino em 21 de May de 2018 às 13:50

May 19, 2018

Thiago Avelino

The 5am club? Just crazy to do it

Have you ever heard of #the5amclub? Do you know how powerful the range of 5 to 8 A.M.?

Sorry tweet in pt-BR

Few people are willing to wake up at 5 A.M. by choice, but that’s exactly what makes the most successful people in the world, the 5am club class, the high achievers.

Who does this madness? Tim Cook (Apple’s CEO), Richard Branson (from Virgin), Robert Iger (from Disney), Howard Schultz (from Starbucks) and Benjamin Franklin are some of the people who adhered to this lifestyle.

Understand why every day the club wins more fans!

por Avelino em 19 de May de 2018 às 16:06

May 17, 2018

PythonClub

Upload de Arquivos com Socket e Struct

Apesar de termos muitas formas de enviarmos arquivos para servidores hoje em dia, como por exemplo o scp e rsync, podemos usar o python com seus modulos built-in para enviar arquivos a servidores usando struct para serializar os dados e socket para criar uma conexão cliente/servidor.

Struct

O modulo struct é usado para converter bytes no python em formatos do struct em C. Com ele podemos enviar num unico conjunto de dados o nome de um arquivo e os bytes referentes ao seus dados.

Struct também é utilizado para serializar diversos tipos de dados diferentes, como bytes, inteiros, floats além de outros, no nosso caso usaremos apenas bytes.

Vamos criar um arquivo para serializar.

!echo "Upload de arquivos com sockets e struct\nCriando um arquivo para serializar." > arquivo_para_upload

Agora em posse de um arquivo vamos criar nossa estrutura de bytes para enviar.

arquivo = "arquivo_para_upload"
with open(arquivo, 'rb') as arq:
    dados_arquivo = arq.read()
    serializar = struct.Struct("{}s {}s".format(len(arquivo), len(dados_arquivo)))
    dados_upload = serializar.pack(*[arquivo.encode(), dados_arquivo])

Por padrão, struct usa caracteres no inicio da sequencia dos dados para definir a ordem dos bytes, tamanho e alinhamento dos bytes nos dados empacotados. Esses caracteres podem ser vistos na seção 7.1.2.1 da documentação. Como não definimos, será usado o @ que é o padrão.

Nessa linha:

serializar = struct.Struct("{}s {}s".format(len(arquivo), len(dados_arquivo)))

definimos que nossa estrutura serializada seria de dois conjuntos de caracteres, a primeira com o tamanho do nome do arquivo, e a segunda com o tamanho total dos dados lidos em

dados_arquivo = arq.read()

Se desempacotarmos os dados, teremos uma lista com o nome do arquivo e os dados lidos anteriormente.

serializar.unpack(dados_upload)[0]
b'arquivo_para_upload'
serializar.unpack(dados_upload)[1]
b'Upload de arquivos com sockets e struct\\nCriando um arquivo para serializar.\n'

Agora de posse dos nossos dados já serializados, vamos criar um cliente e um servidor com socket para transferirmos nosso arquivo.

Socket

O modulo socket prove interfaces de socket BSD, disponiveis em praticamente todos os sistemas operacionais.

Familias de sockets

Diversas familias de sockets podem ser usadas para termos acessos a objetos que nos permitam fazer chamadas de sistema. Mais informações sobre as familias podem ser encontradas na seção 18.1.1 da documentação. No nosso exemplo usaremos a AF_INET.

AF_INET

AF_INET precisa basicamente de um par de dados, contendo um endereço IPv4 e uma porta para ser instanciada. Para endereços IPv6 o modulo disponibiliza o AF_INET6

Constante [SOCK_STREAM]

As constantes representam as familias de sockets, como a constante AF_INET e os protocolos usados como parametros para o modulo socket. Um dos protocolos mais usados encontrados na maioria dos sistemas é o SOCK_STREAM.

Ele é um protocolo baseado em comunicação que permite que duas partes estabeleçam uma conexão e conversem entre si.

Servidor e cliente socket

Como vamos usar um protocolo baseado em comunicação, iremos construir o servidor e cliente paralelamente para um melhor entendimento.

Servidor

Para esse exemplo eu vou usar a porta 6124 para o servidor, ela esta fora da range reservada pela IANA para sistemas conhecidos, que vai de 0-1023.

Vamos importar a bilioteca socket e definir um host e porta para passarmos como parametro para a constante AF_INET.

import socket
host = "127.0.0.1"
porta = 6124
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Agora usaremos o metodo bind para criarmos um ponto de conexão para nosso cliente. Esse metodo espera por uma tupla contento o host e porta como parametros.

sock.bind((host, porta))

Agora vamos colocar nosso servidor socket em modo escuta com o metodo listen. Esse metodo recebe como parametro um numero inteiro (backlog) definindo qual o tamanho da fila que será usada para receber pacotes SYN até dropar a conexão. Usaremos um valor baixo o que evita SYN flood na rede. Mais informações sobre backlog podem ser encontradas na RFC 7413.

sock.listen(5)

Agora vamos colocar o nosso socket em um loop esperando por uma conexão e um inicio de conversa. Pra isso vamos usar o metodo accept que nos devolve uma tupla, onde o primeiro elemento é um novo objeto socket para enviarmos e recebermos informações, e o segundo contendo informações sobre o endereço de origem e porta usada pelo cliente.

Vamos criar um diretório para salvar nosso novo arquivo.

!mkdir arquivos_recebidos

Os dados são enviados sempre em bytes. Leia os comentários

while True:
    novo_sock, cliente = sock.accept()
    with novo_sock:  # Caso haja uma nova conexão
        ouvir = novo_sock.recv(1024)  # Colocamos nosso novo objeto socket para ouvir
        if ouvir != b"":  # Se houver uma mensagem...
            """
            Aqui usaremos os dados enviados na mensagem para criar nosso serielizador.

            Com ele criado poderemos desempacotar os dados assim que recebermos.
            Veja no cliente mais abaixo qual a primeira mensagem enviada.
            """
            mensagem, nome, dados = ouvir.decode().split(":")
            serializar = struct.Struct("{}s {}s".format(len(nome.split()[0]), int(dados.split()[0])))
            novo_sock.send("Pode enviar!".encode())  # Enviaremos uma mensagem para o cliente enviar os dados.
            dados = novo_sock.recv(1024)  # Agora iremos esperar por eles.
            nome, arquivo = serializar.unpack(dados)  # Vamos desempacotar os dados
            """
            Agora enviamos uma mensagem dizendo que o arquivo foi recebido.

            E iremos salva-lo no novo diretório criado.
            """
            novo_sock.send("Os dados do arquivo {} foram enviados.".format(nome.decode()).encode())
            with open("arquivos_recebidos/{}".format(nome.decode()), 'wb') as novo_arquivo:
                novo_arquivo.write(arquivo)
                print("Arquivo {} salvo em arquivos_recebidos.".format(nome.decode()))

    Arquivo arquivo_para_upload salvo em arquivos_recebidos.

Cliente

Nosso cliente irá usar o metodo connect para se connectar no servidor e a partir dai começar enviar e receber mensagens. Ele também recebe como parametros uma tupla com o host e porta de conexão do servidor.

host = '127.0.0.1'
porta = 6124
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # Cria nosso objeto socket
sock.connect((host, porta))
sock.send("Enviarei um arquivo chamado: {} contendo: {} bytes".format(
    arquivo, len(dados_arquivo)).encode())  # Enviamos a mensagem com o nome e tamanho do arquivo.
ouvir = sock.recv(1024)  # Aguardamos uma mensagem de confirmação do servidor.
if ouvir.decode() == "Pode enviar!":
    sock.send(dados_upload)  # Enviamos os dados empacotados.
    resposta = sock.recv(1024)  # Aguardamos a confirmação de que os dados foram enviados.
    print(resposta.decode())

    Os dados do arquivo arquivo_para_upload foram enviados.

Agora podemos checar nossos arquivos e ver se eles foram salvos corretamente.

!md5sum arquivo_para_upload; md5sum arquivos_recebidos/arquivos_para_upload

    605e99b3d873df0b91d8834ff292d320  arquivo_para_upload
    605e99b3d873df0b91d8834ff292d320  arquivos_recebidos/arquivo_para_upload

Com base nesse exemplo, podem ser enviados diversos arquivos, sendo eles texto, arquivos compactados ou binários.

Sem mais delongas, fiquem com Cher e até a próxima!

Abraços.

por Silvio Ap Silva em 17 de May de 2018 às 22:24

May 16, 2018

Thiago Avelino

Medo? Lute contra os seus medos e mude seu futuro

Por onde começar? Que tal essa enquete abaixo!

AVISO: Estou expondo minha opinião pessoal (recomendo considerar como loucura da minha cabeça)

Analisando o resultado da enquete fica extremamente claro que o medo de muitos é o fracasso, não é nada fácil combater esse medo (mas é necessário para uma evolução pessoal), uma das formas de combater qualquer medo é encarar ele de frente, isso não quer dizer que você não tem medo e sim que esta batendo de frente com você mesmo.

Continue lendo em avelino.xxx.

por Avelino em 16 de May de 2018 às 23:34

May 15, 2018

PythonClub

Monitorando Ips Duplicados na Rede

Muitos administradores de redes e sysadmins encontram problemas de conectividade nos ambientes que administram e por muitas vezes o problema é um simples IP duplicado causando todo mal estar. Agora veremos como usar o scapy e defaultdict da lib collections para monitorar esses IPs.

Scapy

O Scapy é uma poderosa biblioteca de manipulação de pacotes interativa, com abrangencia a uma enorme quantidade de protocolos provenientes da suite TCP/IP. Mais informações sobre o scpay pode ser encontrada na documentação oficial. Nesse caso em especifico iremos utilizar do scapy a metaclasse ARP e a função sniff.

from scapy.all import ARP, sniff

sniff

Vamos usar a função sniff para monitorar os pacotes que trafegam na rede usando o protocolo ARP. Pra isso vamos utilizar dela quatro parametros basicos:

sniff(prn=pacotes, filter="arp", iface=interface, timeout=10)
  • prn, chama uma função para ser aplicada a cada pacote capturado pelo sniff.

  • filter, irá filtrar todos os pacotes que contiverem o protocolo ARP.

  • iface, determina a interface de rede que será monitorada.

  • timeout, irá determinar que nosso monitoramento da rede se dara por 60 segundos.

ARP

ARP é uma metaclasse de pacotes com dados sobre o protocolo arp pertencente a camada de enlace de dados. Iremos utilizar essa metaclasse para filtrar os pacotes com informações de pacotes com respostas a requisições arp. (opcode == 2 [is at]) As informações sobre o protocolo ARP podem serm encontradas na rfc826 no site do IETF.

collections.defaultdict

defaultdict é uma subclasse de dict que prove uma instancia de variavel para a chamada de uma chave inexistente.

from collections import defaultdict
list_ips = defaultdict(set)

Basicamente nossa função irá monitorar por um certo tempo o trafego de pacotes pela rede adicionar a nossa variavel list_ips o endereço ou endereços MAC encontrados.

Definindo a função que será passada como parametro para o sniff.

Para cada pacote capturado pela função sniff, será checado se o opcode corresponde a um response do protocolo arp. Caso seja, sera adicionado a nossa defaultdict.

def pacotes(pacote):
    """Checa se o valor do opcode dentro do protocolo arp é igual a 2."""
    if pacote[ARP].op == 2:
        # Se for adiciona o ip de origem e seu mac à dict list_ips
        list_ips[pacote[ARP].psrc].add(pacote[ARP].hwsrc)

Limpando a tabela arp

Para que seja feita novas requisições arp, iremos limpar nossa tabela arp e iniciar o monitoramento da rede. Pra isso iremos usar o comando arp, dentro do shell do sistema. (Como uso FreeBSD vou definir uma função chamando um comando pelo csh)

import os
os.system('which arp')
/usr/sbin/arp

Com posse do caminho do comando arp, irei definir uma função que limpe a tabela e inicie o monitore a rede por 60 segundos.

def monitorar(interface):
    """
    O comando arp no FreeBSD usa os parametros:

    -d para deletar as entradas
    -i para declarar a interface
    -a para representar todas entradas a serem deletas.
    """
    cmd = "/usr/sbin/arp -d -i {} -a".format(interface)
    os.system(cmd)
    sniff(prn=pacotes, filter="arp", iface=interface, timeout=10)

E por ultimo chamar a função de monitoramento. No meu caso eu vou monitorar a interface em0.

monitorar("em0")

Agora só conferir as entradas em nossa dict.

for ip in list_ips:
    print "IP: {} -> MACs: {}".format(ip, ", ".join(list(list_ips[ip])))

IP: 192.168.213.1 -> MACs: 00:90:0b:49:3d:0a
IP: 192.168.213.10 -> MACs: 08:00:27:bf:52:6d, a0:f3:c1:03:74:6a

Eu uso um script rodando nos switchs e gateway da rede que me enviam mensagens assim que ips duplicados são encontrados na rede. Também da pra usar o arping do scpay para fazer as requisições arp e coletar os responses.

Abraços.

por Silvio Ap Silva em 15 de May de 2018 às 13:24

May 01, 2018

Lauro Moura

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

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

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

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

Templates

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

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

#include <iostream> // Para imprimir na tela

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

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

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

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

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

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

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

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

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

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

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

Iteradores

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

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

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

range-begin-end

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

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

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

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

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

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

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

o/

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

April 25, 2018

Humberto Rocha

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

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

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

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

Agora vem a dica importante:

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

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

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

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

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

Bônus

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

25 de April de 2018 às 00:00

April 19, 2018

Magnun Leno

Algumas Razões Para Amar o PostgresSQL

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

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

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

April 15, 2018

Humberto Rocha

Autenticação JWT com Angular e Django

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

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

Autenticação

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

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

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

Para nos auxiliar com estes problemas utilizaremos o JWT.

JWT

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

header.payload.signature

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

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

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

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

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

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

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

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

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PcmVIPbcZl9j7qFzXRAeSyhtuBnHQNMuLHsaG5l804A

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

Authorization: JWT <token>

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

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

coding cat

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

Backend

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

$ pip install djangorestframework-jwt

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

from datetime import timedelta

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

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

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

from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token

urlpatterns = [
    ...

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

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

denied

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

Frontend

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

// app.component.ts

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

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

  constructor() { }

  ngOnInit() {
  }
}

// login.component.ts

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

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

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

  error: any;

  constructor() { }

  ngOnInit() {
  }

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

// app-routing.module.ts

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

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

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

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

// app.module.ts

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

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

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

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

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

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

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

// auth.service.ts

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

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

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

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

@Injectable()
export class AuthService {

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

  constructor(private http: HttpClient) { }

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

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

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

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

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

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

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

    return moment(expiresAt);
  }

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

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

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

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

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

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

@Injectable()
export class AuthGuard implements CanActivate {

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

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

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

      return false;
    }
  }
}

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

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

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

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

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

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

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

// app.module.ts

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

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

// app-routing.module.ts

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

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

// login.component.ts

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

...
export class LoginComponent implements OnInit {

  error: any;

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

  ngOnInit() {
  }

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

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

auth

it works

Conclusão

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

15 de April de 2018 às 00:00

April 09, 2018

Flavio Ribeiro

Trusting the Magic of Beginnings

Trusting the Magic of Beginnings

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

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

Trusting the Magic of Beginnings

morning Times from @mynytimes
My Journey in a Nutshell

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

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

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

Trusting the Magic of Beginnings

Enjoying Demuxed with Said and Thompson on Crunchyroll HQ

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

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

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

Coming Next

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

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

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

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

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

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

April 05, 2018

Aprenda Python

* Entendendo virtualenvs

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

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

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

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

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

* Como hospedar meu site feito em Python?

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

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

April 02, 2018

Thiago Avelino

Guia de estilo para pacotes Go

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

One code style to rule all

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

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

Continue lendo em avelino.xxx.

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

March 27, 2018

Thiago Avelino

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

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

Continue lendo em avelino.xxx.

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

March 26, 2018

Thiago Avelino

Empreendendo em seu local de trabalho

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

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

Continue lendo em avelino.xxx.

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

March 19, 2018

Ricbit

A Marcha dos Lemmings

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

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


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

As Regras do Jogo


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


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

ABCABCA
ABCACAB
ABCACAC
ACABCAB
ACABCAC
ACACABC
ACACACA

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

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

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

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

Força Bruta


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

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

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

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

Multiplicação de Matrizes


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

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

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

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

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

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

Função Geradora


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

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

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

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

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

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

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

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

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

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

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

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

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

Um Problema em Aberto


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

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

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

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

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

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

March 11, 2018

Filipe Saraiva

Procurando recomendações de “distros KDE”

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

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

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

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

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

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

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

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

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

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

March 05, 2018

Humberto Rocha

TLDR: Publicando no PyPI

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

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

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

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

from setuptools import find_packages, setup

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

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

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

Empacotando

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

$ python setup.py sdist

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

Para já empacotar o código montado rode:

$ python setup.py bdist_wheel

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

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

Publicando

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

$ pip install twine

Em seguida basta rodar o seguinte comando:

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

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

Bônus - Versionamento

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

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

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

import os
import re

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

Bônus - README.md para rst

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

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

$ pip install pypandoc

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

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

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

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

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

05 de March de 2018 às 00:00

March 01, 2018

Thiago Avelino

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

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

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

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

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

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

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

Como tudo começou?

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

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

Linha do tempo de linguagens que segue o dialeto Lisp

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

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

Veja a timeline visual

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

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

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

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

Por onde começar estudar Lisp?

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

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

Segue alguns links:

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

Originally published at avelino.xxx.

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

February 26, 2018

Filipe Saraiva

Papo Livre sobre KDE

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

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

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

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

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

Python Help

Preservando a ordem de sequências ao remover duplicatas

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

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

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

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

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

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

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

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

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

>>> odict = OrderedDict.fromkeys(urls)

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

Como resultado, temos:

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

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

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

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

>>> list(odict.keys())

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

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

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

February 17, 2018

Humberto Rocha

TLDR: Executando comandos em multiplos hosts com Fabric

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

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

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

Para instalar o Fabric em python 2 rode:

$ pip install Fabric

Ou para python 3:

$ pip install Fabric3

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

from fabric.api import env, run

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


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

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

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

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

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

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


Done.

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

$ fab disk_usage -P

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

Bônus

Dica do @luizirber no twitter.

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

$ fab [options] -- [shell command]

O comando ficaria desta forma:

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

17 de February de 2018 às 00:00

February 16, 2018

PythonClub

Django Rest Framework - #3 Class Based Views

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

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

Veja como fica a views.py:

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


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

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

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


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

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

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

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

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

E urls.py:

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

Usando Mixins

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

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


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

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

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


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

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

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

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

Usando generic class-based views

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

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


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


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

Versão final de views.py.

Abraços.

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

February 15, 2018

PythonClub

Django Rest Framework - #2 Requests and Responses

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

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

Objeto Response

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

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

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

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

Nosso código ficou assim:

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


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

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


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

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

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

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

Veja no GitHub.

Usando sufixo opcional

Em core/urls.py acrescente

from rest_framework.urlpatterns import format_suffix_patterns

...

urlpatterns = format_suffix_patterns(urlpatterns)

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

def person_list(request, format=None):

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

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

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

Até a próxima.

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

February 13, 2018

Humberto Rocha

Separando o Frontend do Backend com Angular e Django

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

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

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

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

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

Separando as Responsabilidades

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

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

Backend

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

Frontend

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

Implementando o backend

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

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

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

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

novo projeto

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

$ pip install djangorestframework

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

INSTALLED_APPS = (
    ...
    'rest_framework',
)

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

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

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

rest login

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

python manage.py startapp shopping

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

# settings .py

INSTALLED_APPS = (
    ...
    'shopping',
)

# models.py

from django.db import models


class ShoppingItem(models.Model):

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

# serializers.py

from rest_framework import serializers

from .models import ShoppingItem


class ShoppingItemSerializer(serializers.ModelSerializer):

    class Meta:
        model = ShoppingItem
        fields = '__all__'

# views.py

from rest_framework import viewsets

from .models import ShoppingItem
from .serializers import ShoppingItemSerializer


class ShoppingItemViewSet(viewsets.ModelViewSet):

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

# urls.py

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

from shopping.views import ShoppingItemViewSet

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

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

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

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

E voilá, nosso backend está funcionando:

shopping list

Implementando o frontend

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

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

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

home angular

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

// shopping-item.interface.ts

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

// api.service.ts

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

@Injectable()
export class ApiService {

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

  constructor(private http: HttpClient) { }


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

//  app.component.ts

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

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

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

  items: ShoppingItem[];
  error: any;

  constructor(private api: ApiService) { }

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

// app.module.ts

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

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


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

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

ops

wtf

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

cors error

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

xeroque holmes

Cross-Origin Resource Sharing (CORS)

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

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

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

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

$ pip install django-cors-headers

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

INSTALLED_APPS = (
    ...
    'corsheaders',
)

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

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

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

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

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

funcionou

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

// api.service.ts

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

@Injectable()
export class ApiService {

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

  constructor(private http: HttpClient) { }


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

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

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

//  app.component.ts

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

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

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

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

  items: ShoppingItem[];
  error: any;

  constructor(private api: ApiService) { }

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

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

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

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

lista de compras

Conclusão

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

13 de February de 2018 às 00:00

January 19, 2018

Ricbit

A Intuição do Knuth

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


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

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

Eu e Knuth, o trollzinho

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

Demonstrações construtivas e não-construtivas


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

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

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

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

A Teoria de Shannon aplicada à busca binária


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

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

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

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

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

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

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


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

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

O limite da ordenação


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

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

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

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

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

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

A análise por quantidade de informação


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

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

O dia que o Knuth ganhou meu cheque

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



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


A Soma dos Quadrados


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

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


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

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

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

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


O Cálculo Discreto


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

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

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

Interpolação de polinômios


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

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

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

O cheque do Knuth


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

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

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


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


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

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

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

December 24, 2017

PythonClub

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

2. Iteráveis e iteradores

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

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

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

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

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

# 1
# 2
# 3
# 4
# 5

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

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

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

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

2.1 __getitem__

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

Vamos exemplificar:

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

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

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

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

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

2.2 __iter__

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

Uma classe que implementa __iter__:

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

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

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

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

    def __iter__(self):
        return self

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

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

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

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

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

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

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

December 16, 2017

Filipe Saraiva

Sprint do KDE Edu 2017

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

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

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

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

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

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

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

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

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

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

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

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

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

December 08, 2017

PythonClub

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

1. Funções

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

1.1 Funções como objeto de primeira classe

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

# uma lista

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

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

# Funções como objeto de primeira classe

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

def func_2(x):
    return x + 2

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

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

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

1.2 Funções puras

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

valor = 5

def mais_cinco(x):
    return x + valor

assert mais_cinco(5) == 10 # True

valor = 7

assert mais_cinco(5) == 10 # AssertionError

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

valor = 5

def mais_cinco(x):
    return x + 5

assert mais_cinco(5) == 10 # True

valor = 7

assert mais_cinco(5) == 10 # True

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

valor = 5

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

print(valor) # 7

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

1.3 Funções de ordem superior (HOFs)

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

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

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

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

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

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

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

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

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

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

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

1.3.1 Um exemplo usando funções embutidas:

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

def func(arg):
    return arg + 2

lista = [2, 1, 0]

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

Mas fique tranquilo, falaremos muito mais sobre isso.

1.4 __call__

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

Em uma função anônima:

func = lambda x: x

'__call__' in dir(func) # True

Em funções tradicionais:

def func(x):
    return x

'__call__' in dir(func) # True

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

SIIIIIM. Chupa Haskell

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

1.5 Funções geradoras

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

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

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

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

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

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

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

1.6 Funções anônimas (lambda)

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

lambda argumento: argumento

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

def func():
  pass

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

def func():
  pass

func.__name__ # func

lambda_func = lambda arg: arg

lambda_func.__name__ # '<lambda>'

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

def func():
  pass

type(func) # function

lambda_func = lambda arg: arg

type(lambda_func) # function

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

valor_1 if condicao else valor_2

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

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

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

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

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

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

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

November 28, 2017

Bruno Cezar Rocha

py2rs - from Python to Rust - Reference Guide

py2rs

From Python into Rust

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

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

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

Monty Python - Season 3 - Episode 49

The sketch

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

TOC

Getting Started with Rust

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

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

  5. Read some real examples

  6. Patterns and Good Practices

Exercices

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

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

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

Getting updated

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

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

Interact with other Rustaceans

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

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

Local

Additional learning resources

Facts

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

ferris

Glossary of terms

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

py2rs

From Python into Rust

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

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

General

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

Environment Tools

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

Libraries and Frameworks

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

Applications

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

Useful crates

Add Pythonic features to Rust

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

Show me The code

From Python to Rust by examples

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

Creating a new project

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

Python

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

Rust

$ cargo new my-rust-program

Installing new libraries/crates

Python

$ pip install foo

Rust

$ cargo install foo

Running / Compiling

Python

$ python my_python_program.py

Rust

$ cargo run

Hello World

Python

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

Rust

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

Types and Declarations

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

Python

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

Rust

use std::collections::HashMap;

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

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

Define a function

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

Python

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

Rust

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

List/Slice

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

Python

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

for name in names:
    print(name)

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

Rust

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

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

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

Dict/Map

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

Python


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

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

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

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

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

del ages['taz']

for name in ages:  # Keys
    print(name)

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

Rust

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

fn main() {

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

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

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

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

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


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

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

}

Pythonic alternative to dict/map in Rust

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

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

then

#[macro_use] extern crate maplit;

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

set / HashSet

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

Python


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

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

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


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

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

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

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

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

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

Rust

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

fn main() {

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

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

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

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

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

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

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

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

or syntax sugared using maplit crate

#[macro_use] extern crate maplit;

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

while and for loops

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

Python

# While loop

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

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

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


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

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

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

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

Rust

fn main() {

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

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

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

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

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

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

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

Loop Labels

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

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

Files

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

Python

from pathlib import Path

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

Rust

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


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

Exceptions/Return Error

Expecting for exceptions and identifying errors.

Python

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

# ...

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

Rust

```

---

### Concurrency

**Python**

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

Rust

```

---

### Communicating between threads

Managing data context between threads.

**Python**

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


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

Rust

```

---

### Sorting

Sorting lists, reversing and using a key.

**Python**

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

# Lexicographical order
names.sort()

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

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

Rust

```

---

### Web app with Flask / Rocket

**Python**

```python
from flask import Flask

app = Flask(__name__)


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


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

Rust

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

extern crate rocket;

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

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

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

HTTP Request with error handling

Python

import json
from urlib2 import urlopen

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

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

print(reply['origin'])

Rust

```

---

### Encode and Decode JSON

**Python**

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

json.dump(obj, stdout)

Rust

```


---


### Print Object for Debug/Log 

**Python**

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

Rust

```

---

### Object Orientation

**Python**

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

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

# ...

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

Rust

rust


Credits

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

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

With contributions by:

  • Send a PR and include your name and links

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

Lauro Moura

[C] Varargs e passando eles adiante

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

Exemplo básico:

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

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

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

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

    va_end(args);
    return acc;
}

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

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

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

CUIDADO!

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

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

Passando adiante

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

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

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

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

    int acc = vsum_all(count, args);

    va_end(args);
    return acc;
}

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

o/

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