Java ou Python? Jython!

O interpretador Python padrão, também conhecido como CPython, é implementado em linguagem C, e nada mais é do que um programa que roda diretamente sobre o hardware. Além do CPython, existem implementações alternativas do interpretador Python. Talvez a mais popular dentre elas, o Jython é escrito em Java e roda sobre a JVM. Além de poder ser executado em qualquer ambiente que possua uma JVM instalada, Jython possibilita que APIs Java sejam utilizadas em Python. Ou seja, tenta unir o melhor de dois mundos: a rapidez no desenvolvimento de Python com a enorme quantidade de APIs maduras disponíveis para Java. Por se tratar de um interpretador Python, a sintaxe dos programa escritos para rodar sobre o Jython é idêntica a dos programas escritos para rodar sobre o CPython.

Mas como funciona isso?

É bem simples, basta importar as libs Java e sair usando, com a sintaxe de Python. Veja:

>>> from java.lang import Math, System
>>> raiz = Math.sqrt(9)
>>> System.out.println(raiz)
3.0

O trecho de código acima foi executado no interpretador Jython (disponível em jython.org).

Viu que maravilha? Importamos e executamos o código de bibliotecas Java, mas com a sintaxe de Python!

Herança

Outra coisa fantástica é a possibilidade de estender classes Java usando Python. Por exemplo, considere a seguinte classe Java (copiada descaradamente daqui):

public class Bicycle {

    public int cadence;
    public int gear;
    public int speed;

    public Bicycle(int startCadence, int startSpeed, int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
    }

    public void setCadence(int newValue) {
        cadence = newValue;
    }

    public void setGear(int newValue) {
        gear = newValue;
    }

    public void applyBrake(int decrement) {
        speed -= decrement;
    }

    public void speedUp(int increment) {
        speed += increment;
    }
}

Se quisermos estender a classe acima, especializando-a para representar um tipo específico de bicicleta, como uma mountain bike, podemos estendê-la como se fosse uma classe Python. Veja abaixo o exemplo (MountainBike.py):

import Bicycle

class MountainBike(Bicycle):

    def __init__(self, seatHeight, startCadence, startSpeed, startGear):
        super(Bicycle, self).__init__(self, startCadence, startSpeed, startGear)
        self.seatHeight = seatHeight

    def setHeight(self, seatHeight):
        self.seatHeight = seatHeight

Se quiser testar os exemplos acima, não esqueça de:

  1. Compilar o arquivo Bicycle.java com um compilador Java:
    • javac Bicycle.java
  2. Rodar o arquivo MountainBike.py com o jython:
    • jython MountainBike.py

Todo programa Python vai rodar no Jython?

Não. Alguns programas rodam somente na implementação padrão do interpretador, pois dependem de módulos específicos que foram escritos em C e que ainda não foram portados para Java. Mas, de acordo com o FAQ, a grande maioria já está disponível.

Por que usar Jython?

Odeia a sintaxe e exigências semânticas de Java, mas não desgruda por causa das APIs que ela oferece? Manda ver com Jython! As possibilidades são muitas! Que tal reaproveitar aquela classe Java que você criou há tempos e que vem quebrando um galhão nos seus projetos? Basta importá-la como se fosse um módulo Python e chamar seus métodos.

Outros casos de uso para Jython podem ser protótipos de aplicações explorando bibliotecas Java, implantação de aplicações Django em containers Java como Tomcat/Jboss/Glassfish, usando recursos desses ambientes e integrando com outras aplicações Java já existentes, e por último mas não menos importante: tornar sua aplicação Java extensível permitindo que os usuários adicionem comportamento escrevendo scripts em Python.

Mais informações

Obrigado ao Elias Dorneles pela colaboração!

Anúncios

Pegadinha em valores default em funções

Se você é desenvolvedor Python, já deve ter visto muitas funções que definem valores padrão para alguns de seus argumentos. Por exemplo:

def power(base, exp=2):
    return base**exp

É bem comum usarmos esse artifício quando queremos que determinado parâmetro tenha um valor mesmo que o chamador não tenha passado valor algum para ele. Isso funciona muito bem, porém, pode gerar uma certa confusão quando o tipo do valor default do parâmetro em questão for mutável. Veja o trecho de código abaixo:

def func(valor, seq=[]):
    seq.append(valor)
    return seq

print func(10)
print func(20)
print func(30)

Antes de executar o código acima, responda: o que será impresso pelo código acima?

Se você respondeu

[10]
[20]
[30]

você está errado, pois o resultado é:

[10]
[10, 20]
[10, 20, 30]

Observe que, na segunda chamada à função func(), a lista seq manteve o valor 10 como elemento e então adicionou o valor 20 ao seu final. Mas por quê, se seq possui uma lista vazia [] como valor default? Este valor não deveria ser atribuído a seq a cada chamada de função?

A resposta é, como tudo em Python, consistente com a linguagem. Em Python, valores default de parâmetros são avaliados somente no momento em que a função estiver sendo avaliada (em sua definição), e não a cada chamada à mesma.

Ao encontrar a palavra-chave def, o interpretador Python avalia a expressão seguinte como uma função e então cria em memória um objeto function referente à função definida. Assim, a atribuição seq=[] é feita pelo interpretador nesse momento, e não a cada vez que func for chamada.

Vamos ver o que acontece: quando func é chamada pela primeira vez, a lista referenciada por seq possui o valor []. Então, dentro da função é feito um append em seq do valor recebido como parâmetro. Como seq é uma referência para um objeto mutável (uma lista), a lista referenciada por seq é quem tem adicionada a si o valor passado como parâmetro. Na chamada seguinte, seq continua apontando para o mesmo objeto lista, que agora possui um elemento (o valor 10) e então o valor 20 será, dentro da função, adicionado ao final da lista referenciada por seq. Na última chamada, é adicionado o valor 30 ao final da lista apontada por seq.

O que deve ser lembrado sempre é que valores default para parâmetros em uma função são avaliados somente na definição da mesma, e não a cada chamada.

Como driblar isso?

Se você precisar que um parâmetro tenha o valor [] quando o chamador não passar valor algum a ele, você pode fazer o seguinte:


def func(valor, seq=None):
    if seq is None:
        # chamador não forneceu valor para seq
        seq = []
    seq.append(valor)
    return seq

Leia mais

Detalhes como o apresentado neste post são tratadas em alguns livros, como os excelentes:

Obrigado ao Elias Dorneles pela revisão!

Um blog com Google App Engine

Sumário

Este é um post-tutorial, onde vou descrever o desenvolvimento de um sistema de blogging bem simples usando os recursos nativos do Google App Engine (GAE, para os amigos). Caso não tenha familiaridade com o GAE, sugiro a leitura dos textos anteriores que publiquei aqui no Python Help sobre o assunto:

Com as leituras acima, você terá a base necessária para entender o tutorial aqui apresentado.

O que vamos desenvolver?

O objetivo é implementar um sistema de blogging bem simples, com as seguintes funcionalidades:

  • Tela principal com listagem de todos os posts
  • Tela para adicionar um novo post
  • Tela para visualizar um post
  • Restrição de acesso para a tela de criar post

A tela para adicionar posts deve redirecionar para o post recém criado.

Mãos à obra

A primeira coisa que temos que fazer é criar o projeto, ou seja, definir a estrutura de arquivos que nosso projeto vai ter e configurá-lo no arquivo app.yaml, como já vimos nos posts anteriores. Veja abaixo a estrutura de diretórios que vamos utilizar em nosso projeto:

├── app.yaml
├── main.py
├── models.py
├── static
│   └── main.css
└── templates
    ├── base.html
    ├── detail.html
    ├── form.html
    └── front.html

Crie o arquivo de configurações app.yaml com as seguintes opções:

application: pythonhelp-blog
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: main.app

Como você deve lembrar, a diretiva handlers no arquivo de configuração app.yaml configura como nossa aplicação vai tratar as requisições HTTP vindas do cliente. Aqui configuramos nossa aplicação para servir arquivos estáticos a partir do diretório static e para publicar a aplicação criada na variável app dentro do módulo main.

Ah, não se esqueça de alterar o seu application id (valor de application no app.yaml), pois esse identificador deve ser único no GAE. Ou seja, se eu colocar esse sisteminha no ar com o app id "pythonhelp-blog", você não poderá colocá-lo no ar com o mesmo identificador.

O modelo de dados

Agora, vamos modelar o nosso problema. Como iremos representar as informações no nosso banco de dados? De acordo com os requisitos, iremos precisar de um model para representar um post, que tenha vinculado a si alguns atributos, incluindo o autor do texto (que deve ser um usuário cadastrado).

Graças ao GAE, não precisaremos criar uma entidade User, pois ele já fornece um serviço de gerenciamento e autenticação de usuários (usando contas de usuário na google mesmo). Dessa forma, teremos apenas um model de uma entidade que iremos chamar de BlogPost, contendo um atributo do tipo UserProperty, fornecido pelo GAE. Crie um model BlogPost de acordo com o código a seguir e salve no arquivo models.py:

from google.appengine.ext import db

class BlogPost(db.Model):
    subject = db.StringProperty(required=True)
    author = db.UserProperty(required=True,
                             auto_current_user=True)
    content = db.TextProperty(required=True)
    created = db.DateTimeProperty(auto_now_add=True)
    last_updated = db.DateTimeProperty(auto_now=True)

Observe  o campo author. Passamos um atributo nomeado auto_current_user com o valor True. Isso faz com que o campo author seja setado com o usuário atualmente logado quando um objeto do tipo BlogPost for gravado no datastore. Barbadinha, né?

Implementação das ações

Agora vamos definir as URLs que serão utilizadas e para quais classes elas serão mapeadas em nosso projeto.

Defina o mapeamento das URLs para as classes manipuladoras no arquivo main.py:

    app = webapp2.WSGIApplication(
        [("/blog", BlogMainHandler),
         ("/blog/newpost", BlogNewPostHandler),
         ('/blog/post/(\d+)', BlogPostViewHandler),
         ],
        debug=True)

Dessa forma, nosso esquema de URLs será assim:

  • a página principal (que lista todos os posts), será acessível pela URL "/blog";
  • o formulário para criação de um novo post será acessível via "/blog/newpost";
  • um post em específico será acessível pela URL "/blog/post/id_do_post". Por exemplo, "/blog/post/19" irá abrir uma página de visualização do post cujo id seja 19.

Agora vamos à implementação das classes listadas acima. A primeira delas será a mais simples: BlogMainHandler, que será a página inicial do blog, listando todos os posts.

template_dir = os.path.join(os.path.dirname(__file__),
                            'templates')


class BlogMainHandler(webapp2.RequestHandler):
    def get(self):
        self.response.out.write(
            template.render(template_dir +
                            '/front.html',
                            {'posts': BlogPost.all()}))

A segunda classe que iremos implementar é BlogPostViewHandler, responsável pela visualização de um post em específico.

class BlogPostViewHandler(webapp2.RequestHandler):
  def get(self, post_id):
    try:
      post = BlogPost.get_by_id(int(post_id))
      self.response.out.write(
          template.render(template_dir + '/detail.html',
                          {'post': post}))
    except:
      self.response.out.write("Erro: post não encontrado!")

Veja que o método get recebe um parâmetro post_id. Esse parâmetro é passado pelo URL dispatcher, pegando o pedaço da URL que está entre parênteses na definição ('/blog/post/(\d+)') e repassando como valor ao método (\d+ significa um ou mais dígitos e os parênteses ao redor fazem com que esse valor seja passado ao método get). Por exemplo, um acesso a /blog/post/19 irá fornecer ao parâmetro post_id o valor 19.

Você deve ser capaz de entender os códigos listados acima. Caso tenha dificuldades, reveja os posts em que explico como criar um projeto na GAE, como utilizar o Datastore e o mecanismo de templates.

Depois temos a classe responsável pela criação de novos posts: BlogNewPostHandler. Veja o código abaixo:


class BlogNewPostHandler(webapp2.RequestHandler):

  def get(self):
    user = users.get_current_user()
    if user is not None:
      self.response.out.write(
          template.render(template_dir + '/form.html',
                          {'title': 'Escrever post'}))
    else:
      self.redirect(
          users.create_login_url(self.request.uri))

  def post(self):
    author = users.get_current_user()
    if author is not None:
      subject = self.request.get('subject')
      content = self.request.get('content')
      if subject and content:
        b = BlogPost(subject=subject, content=content)
        b.put()
        self.redirect("/blog/post/" + str(b.key().id()))
      else:
        error = 'Ambos os campos são obrigatórios'
        self.response.out.write(
            template.render(template_dir + '/form.html',
                            {'title': 'Escrever post', 
                             'error': error,
                             'subject': subject,
                             'content': content}))
    else:
      self.redirect(
          users.create_login_url(self.request.uri))

Essa classe tem alguns detalhes que valem a pena ser observados. Em primeiro lugar, ela possui dois métodos: get e post. Como já vimos anteriormente, o método get será chamado pelo ambiente subjacente quando houver uma requisição do tipo GET à URL mapeada para a classe BlogNewPostHandler e o método post será chamado quando houver uma requisição do tipo POST (normalmente usada para fazer escrita de dados no servidor). Dessa forma, a classe possui duas funcionalidades:

  1. get: ao acessar a url /blog/newpost, o usuário irá receber como resposta um formulário para criação de um novo post.
  2. post: quando o formulário for submetido, é feito uma requisição POST, passando os dados do formulário para criar um objeto BlogPost no servidor.

Repare como é simples a autenticação do usuário no código usando o serviço provido pelo GAE. Veja que a primeira linha de cada método chama users.get_current_user para obter o usuário logado atualmente. Caso não haja usuário logado (se o objeto retornado for None), então redirecionamos o usuário para uma URL própria para fazer o login. Quando estiver rodando no ambiente externo, é apresentada a tela de autenticação dos serviços da Google mesmo. No ambiente de testes, é apresentada uma tela de login como a mostrada na figura abaixo.

Tela de login no ambiente local (powered by GAE)

Tela de login no ambiente local (powered by GAE)

Os templates

Em um projeto GAE, os templates contém o código HTML que será enviado para o usuário nas requisições. Como visto no post anterior sobre o assunto, é possível a introdução de campos dinâmicos para serem preenchidos de acordo com os parâmetros passados na renderização dos templates.

Como todas as páginas terão um cabeçalho padrão, definimos um template base que os demais irão estender. Veja abaixo o código de cada um dos templates:

templates/base.html:

<!DOCTYPE html>
<html>
  <head>
    <link type="text/css" rel="stylesheet" href="/static/main.css" />
    <title>{{ title }}</title>
  </head>
  <body>
    <div class="main-title"><a href="/blog">stummjr Blog</a></div>
    {% block content %}
    {% endblock %}
  </body>
</html>

Dessa forma, basta aos subtemplates detail.html, form.html e front.html a descrição do bloco content.

templates/detail.html (usado para visualização de um post específico):

{% extends 'base.html' %}

{% block content %}
<div class="post">
  <div class="post-heading">
    <div class="post-title">
      {{ post.subject }}
    </div>
    <div class="post-date">
      {{ post.created}}
    </div>
  </div>
  <pre class="post-content">
    {{ post.content }}
  </pre>
</div>
{% endblock %}

templates/form.html (usado para apresentar a interface de criação de um novo post):

{% extends 'base.html' %}
{% block content %}
<form method="post">
  <label for="subject">Assunto:
    <input type="text" name="subject" value="{{ subject }}">
  </label>
  <label for="content">Conteúdo:
    <textarea name="content" rows="10" cols="50">
      {{ content }}
    </textarea>
  </label>
  <button type="submit">Salvar</button>
  <div class="error">
    {{ error }}
  </div>
</form>
{% endblock %}

templates/front.html (lista todos os posts existentes no banco de dados do blog):

{% extends 'base.html' %}

{% block content %}
{% for post in posts %}
  <div class="post">
    <div class="post-heading">
      <div class="post-title">
        <a href="/blog/post/{{ post.key.id }}">{{ post.subject }}</a>
      </div>
      <div class="post-date">
        {{ post.created }}
      </div>
    </div>
    <pre class="post-content">
      {{ post.content }}
    </pre>
  </div>
{% endfor %}
{% endblock %}

Como podemos ver, o código base.html referencia o arquivo de estilos main.css, que não será listado aqui devido ao seu tamanho. Todos os arquivos do projeto podem ser baixados no link que consta no final do post.

Testando o blog

Tendo as classes e os templates definidos, agora basta testar nosso projeto. Para isso, vamos usar o ambiente de testes fornecido pelo GAE. Veja como instalar e usar esse ambiente no primeiro post sobre o GAE feito no blog.

Próximos passos

Não pare por aqui. Leia a documentação oficial do GAE (em pt-br, inclusive) e incremente o projeto, adicionando comentários, categorias, etc. Bom trabalho!

Download dos arquivos do projeto

Obrigado ao Elias Dorneles, pela baita revisão que fez no texto!