@python - Entendendo decorators

  • publicado em 22 de dezembro de 2013

Fala, pessoal. Tranquilidade? Hoje eu vou falar sobre decorators do Python. Os decorators são aquelas coisas, começadas por uma arroba, que você usa «em cima» das suas definições de funções/métodos/classes. Algumas que você já deve ter usado são:

class Classe:

    @classmethod
    def método_de_classe(cls):
        print('bla')

    @property
    def read_only(self):
        return True

Eles servem pra alterar o comportamento das nossas funções/métodos/classes de diversas maneiras. Nos nossos exemplos aí, o primeiro decorator faz com que o nosso método receba a classe e não a instância como primeiro parâmetro, e no segundo, faz com que chamemos nosso método como se fosse um atributo, e se tentarmos fazer uma atribuição a isso, vai dar erro.

Deu pra perceber que dá pra fazer um monte de coisa legal com os decorators, não? E pra usar já vimos também que não é difícil, é só colocar um «@meu_decorator» em cima das nossas declarações, e já era.

Pra escrever também não é difícil, a gente só precisa entender direitinho o que é um decorator e depois fica fácil. Então, vamos lá!

O que é um decorator?

Bom, a definição mais simples de um decorator é a seguinte: Um decorator é um callable que retorna um callable. Ponto. Callable é um cara que você pode «chamar» - tipo coisa() - como uma função, um método… qualquer objeto que tenha o método __call__.

Seguindo por essa linha, podemos fazer assim, o nosso primeiro decorator:

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

def um_decorator(func):
    """
    Um callable que tem um callable como parâmetro
    e retorna um callable.
    """
    # faz nada...
    print('decorating func')
    return func

E, no shell, usamos assim:

>>> @um_decorator
... def some_func():
...     print('oi')
...
decorating func
>>> some_func()
oi
>>>

Vamos parar por aqui e entender o que aconteceu.

Entendendo o funcionamento do decorator

Assim que definimos nossa função, o interpretador “viu” que esta função estava decorada, isto é, havia algo começando por um arroba antes da definição e adicionou às variáveis globais, usando o nome da sua fução, não a função que você definiu, mas o retorno do seu decorator. Isso significa que assim que o interpretador “viu” que sua função estava decorada, ele fez algo que seria tipo isso:

>>> some_func = um_decorator(some_func)
decorating func
>>>

E aqui que está toda a jogada. Agora, quem está usando o nome “some_func” não é mais a função que você definiu, e sim a função que o nosso decorator retornou.

Pra exemplificar melhor, vamos fazer uma segunda versão do decorator:

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

def um_decorator(func):
    """
    Um callable que tem um callable como parâmetro
    e retorna um callable.
    """
    def other_func():
        print('ola')

    return other_func

E no shell fica assim:

>>> @um_decorator
... def some_func():
...     print('oi')
...
>>> some_func
<function um_decorator.<locals>.other_func at 0x7fac11ffe050>
>>> some_func()
ola
>>>

Então, o que acabamos de ver aí é que quem está usando o nome “some_func” não é a função que definimos e sim a função other_func, que definimos dentro do nosso decorator. Legal, né?

O que fizemos até aqui foi inútil, eu sei, mas vamos melhorar daqui pra frente. Prometo. :)

Um decorator melhorzinho

O que a gente vai fazer agora é o seguinte: um decorator pra logar as coisas antes e depois da execução de alguma coisa. Algo mais ou menos assim:

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

def loga(func):
    """
    Decorator que loga a execução do callable
    """
    def loga_execucao(*args, **kwargs):
        print('iniciando execucao com %s, %s' % (str(args), str(kwargs)))
        retorno = func(*args, **kwargs)
        print('terminou execucao com %s' % retorno)

    return loga_execucao

E, novamente, no shell fica assim:

>>> @loga
... def some(a, b):
...     return a + b
...
>>> some(1, 1)
iniciando execucao com (1, 1), {}
terminou execucao com 2
>>> from random import random
>>> @loga
... def do_magic(*args, **kwargs):
...     return random()
...
>>> do_magic(1, 'asdf', nada='não sei', acre=NotImplemented)
iniciando execucao com (1, 'asdf'), {'nada': 'não sei', 'acre': NotImplemented}
terminou execucao com 0.28372230130165577
>>>

O funcionamento básico aqui é a mesma coisa do nosso outro decorator, a função loga retornou a função loga_execucao e esta função está usando o nome da função que foi decorada. Mas, além disso, tiveram umas coisas um pouco diferentes, então vamos parar por aqui, respirar um pouco e ver tudo com calma.

Entendendo o funcionamento do decorator melhorzinho

A primeira coisa diferente que notamos agora é que a função loga_execucao tem como parâmetros *args e **kwargs (linha 8). Isso significa que vale tudo, aceita quaisquer argumentos - Nota à parte: isso, do *args e **kwargs, é MUITO da hora. Isso porque queremos decorar qualquer coisa e qualquer coisa pode ter qualquer argumento.

A outra coisa diferente é o que importa aqui. Na linha 10, a gente chama a func(), que é a função que passamos para o decorator. Apesar de a função “func “ não estar no namespace da função loga_execucao (linhas 8-11), a quando chamamos func(), ela é recuperada do namespace antecessor, isto é, da função loga (linhas 4-12). Então, quando a toda vez que a função loga_execucao for executada, o interpretador vai lembrar que no momento da criação dela, existia um parâmetro chamado func, que é a função que você passou pro decorator.

E é isso, essa é toda a mágica dos decorators. Aqui você já pode fazer muitas coisas legais com eles, mas ainda tem mais!

Um decorator com classe

Bom, agora nós vamos fazer um decorator usando uma classe, não funções. O esquema de funcionamento é o mesmo, um callable que retorna outro callable. O nosso decorator agora será um decorator para cachear funções custosas. Se uma fução demora muito pra executar, deixamos o resultado em memória e da próxima vez já pegamos o resultado computado. O decorator fica mais ou menos assim:

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

CACHE = {}


class cacheado:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        # cacheia a função
        name = self.func.__name__
        cacheado = CACHE.get(name)
        if not cacheado:
            cacheado = self.func(*args, **kwargs)
            CACHE[name] = cacheado

        return cacheado

E no shell fica assim:

>>> @cacheado
... def take_time():
...     lista = []
...     for i in range(100000):
...         lista.insert(0, i)
...     return list
...
>>> take_time
<__main__.cacheado object at 0x7f8c1fd81790>
>>> type(take_time)
<class '__main__.cacheado'>
>>>
>>> timeit.timeit(take_time, number=1)
4.054789036999864
>>> timeit.timeit(take_time, number=1)
9.35900243348442e-06
>>> timeit.timeit(take_time.__call__, number=1)
1.2202999641885981e-05
>>>

Viram? Na primeira vez levou 4 segundos. Da segunda foi instantâneo. Agora vamos entender direito o que aconteceu aí.

Entendendo o decorator com classe

Quando fazemos @cacheado, estamos fazendo algo assim, lembra?

>>> def other_take_time():
...     sleep(10)
...     return True
...
>>> other_take_time = cacheado(other_take_time)
>>> other_take_time
<__main__.cacheado object at 0x7f8c1eca0690>
>>>

Então, como agora nosso decorator é uma classe, a função decorada é uma instância da classe “cacheado”, e quando chamamos a função decorada, estamos na verdade chamando o método __call__ da instância de “cacheado”. Simples também, não?

Um decorator com parâmetros

Vamos fazer um outro decorator “cacheado”, mas agora aceitará como argumento quantos segundos o resultado ficará cacheado. Assim:

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

from time import time

CACHE = {}


class cacheado:

    def __init__(self, tempo):
        """
        Recebe o tempo, em segundos, que o resultado
        da função ficará cacheado.
        """
        self.tempo = tempo

    def __call__(self, func):
        # cacheia a função
        # agora, __call__ será chamado pelo @ na construção
        # da função, isto é, uma vez só, e sendo assim, __call__
        # tem que retornar um callable. Agora, __call__
        # é o nosso decorator.

        def cacheia_resultado(*args, **kwargs):
            name = func.__name__
            agora = time()
            cacheado = CACHE.get(name)
            if not cacheado or ((cacheado[1] + self.tempo) < agora):
                retorno = func(*args, **kwargs)
                cacheado = (retorno, agora)
                CACHE[name] = cacheado
            retorno = cacheado[0]

            return retorno

        return cacheia_resultado

E usamos assim:

>>> @cacheado(10)
... def take_time():
...     lista = []
...     for i in range(100000):
...         lista.insert(0, i)
...     return lista
...
>>> take_time
<function cacheado.__call__.<locals>.cacheia_resultado at 0x7f8c1ec9f680>
>>> timeit.timeit(take_time, number=1)
4.03598285499902
>>> timeit.timeit(take_time, number=1)
0.015197464999801014
>>> # alguns segundos depois
...
>>> timeit.timeit(take_time, number=1)
4.0627139449934475

A coisa aí mudou um pouco de figura agora, vamos parar de novo pra entender.

Entendendo o decorator com parâmetro

Agora, quando fizemos @cacheado(10), fizemos algo assim:

>>> def other_take_time():
...     sleep(10)
...     return True
...
>>> decorator = cacheado(10)
>>> decorator
<__main__.cacheado object at 0x7f8c1eca0590>
>>> other_take_time = decorator(other_take_time)
>>> other_take_time
<function cacheado.__call__.<locals>.cacheia_resultado at 0x7f8c1ec9fd40>
>>>

Aí, o decorator não éra mais uma classe ou uma função, e sim uma instância da classe “cacheado”. Com isso, o método __call__ é chamado na criação da função decorada, e não é mais a função decorada, como no exemplo anterior. A função decorada agora é “cacheia_resultado”, a função que definimos dentro do método __call__. Tricky, mas simples, não? Pouco código pra uma coisa legal dessas… Da hora.

Estamos quase lá, mas vamos fazer mais um, só por curiosidade…

Um decorator com parâmetro opcional

Agora que a gente já sacou como funcionam os decorators, fica mole fazer um com parâmetro opcional. Aposto que você já tá pensando em como se faz. Então vamos escrever logo isso.

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

from time import time

CACHE = {}


class _cacheado:

    def __init__(self, tempo):
        """
        Recebe o tempo, em segundos, que o resultado
        da função ficará cacheado.
        """
        self.tempo = tempo

    def __call__(self, func):
        # cacheia a função
        # agora, __call__ será chamado pelo @ na construção
        # da função, isto é, uma vez só, e sendo assim, __call__
        # tem que retornar um callable. Agora, __call__
        # é o nosso decorator.

        def cacheia_resultado(*args, **kwargs):
            name = func.__name__
            agora = time()
            cacheado = CACHE.get(name)

            if not cacheado or ((cacheado[1] + self.tempo) < agora):
                retorno = func(*args, **kwargs)
                cacheado = (retorno, agora)
                CACHE[name] = cacheado

            retorno = cacheado[0]
            return retorno

        return cacheia_resultado


def cacheado(param):
    """
    param pode ser tanto um callable - no caso de o decorator
    não ser usado com parâmetro ou o tempo, se usado com parâmetro.
    """

    if not callable(param):
        # usou o decorator com parâmetro, assim:
        # cacheado(5)
        decorator = _cacheado(param)
        return decorator

    else:
        # usado sem parâmetro, usaremos o tempo padrão.
        tempo = 10
        decorator = _cacheado(tempo)
        # Ao invés de retornar o decorator, como quando usando
        # com parâmetro, temos que retornar a função decorada
        # pela instância de _cacheado por que a funçaõ 'cacheada'
        # já foi usada como o decorator
        função_decorada = decorator(param)
        return função_decorada

E usamos assim:

>>> @cacheado
... def take_time():
...     lista = []
...     for i in range(100000):
...         lista.insert(0, i)
...
>>> timeit.timeit(take_time, number=1)
4.068460593000054
>>> timeit.timeit(take_time, number=1)
1.612800406292081e-05
>>>
>>> @cacheado(30)
... def other_take_time():
...     sleep(10)
...     return True
...
>>> timeit.timeit(other_take_time, number=1)
10.007121031994757
>>> timeit.timeit(other_take_time, number=1)
1.7487000150140375e-05
>>>

Belezinha, né? Acho que chegamos inteiros ao fim e deu pra sacar que não tem nada de mistério com os decorators, bem ao contrário, certo?

Ficaram dúvidas? Pode perguntar! :)

Hum… ficou grande, né? Será que alguém leu até aqui?

[juca@debianmental:~/mysrc/exemplos/decorator]$ python3
Python 3.3.3 (default, Nov 27 2013, 17:12:35)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from decorator import black_knight
>>> @black_knight
... def multiplica(a, b):
...     return a*b
...
>>> multiplica(2, 3)
None shall pass
>>>
[juca@debianmental:~/mysrc/exemplos/decorator]$ su
Senha:
root@debianmental:/home/juca/mysrc/exemplos/decorator# python3
Python 3.3.3 (default, Nov 27 2013, 17:12:35)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from decorator import black_knight
>>> @black_knight
... def multiplica(a, b):
...     return a*b
...
>>> multiplica(2, 3)
You are a looney.
6
>>>

Kudos pra quem postar o código de “black_knight”!


Criando um daemon com Python

  • publicado em 03 de dezembro de 2013

Fala, pessoal, tranquilidade? Como de costume, fiquei muito tempo sem postar. Pra voltar, vamos ver como criar um daemon usando Python. Mas primeiro…

O que é um daemon?

Um daemon é um processo que fica sendo executado em segundo plano, sem contato interativo com um usuário e desassociado de um tty. O nome daemon vem do Demônio de Maxuel, e foi usado pela primeira vez (em computação, claro) pelo pessoal do projeto MAC[1]. Os daemons estão presentes no Unix desde os primórios. Aquele monte de *d que você vê, como sshd, httpd, crond e etc, são todos daemons.

Como criar um daemon?

Bom, a explicação rápida pra isso é: com o bom e velho fork-off-and-die. Cria-se um uma cópia de um processo, mata-se o processo pai e faz-se o trabalho no processo filho. A explicação longa é a seguinte:

Cria-se um fork e sai do processo pai. Com isso, libera-se o controle de shell se o daemon foi invocado de um. Também atribui-se outro id para o processo filho, fazendo com que ele não seja session leader.

Criar uma nova sessão sem um terminal de controle associado.

Criar outro fork e sair novamente do pai - aquele que foi filho antes para garantir novamente que o processo não será session leader.

Alterar a máscara de arquivos (umask).

Alterar o diretório de trabalho.

Fechar todos os descritores de arquivos descecessários.

E, agora sim executar o seu trabalho.

Por que o segundo fork()?

Bom, essa é uma discussão grande. Geralmente é dito que o segundo fork é necessário para evitar que o seu daemon obtenha um terminal de controle nos SystemV R4. Como esse já é um sistema em desuso, diz-se que o segundo fork é desnecessário. Mas a especificação POSIX diz o seguinte[2]:

The controlling terminal for a session is allocated by the session leader in an implementation-defined manner. If a session leader has no controlling terminal, and opens a terminal device file that is not already associated with a session without using the O_NOCTTY option (see open()), it is implementation-defined whether the terminal becomes the controlling terminal of the session leader

Então, como é uma questão de implementação, prefiro continuar usando um segundo fork. :) Agora, vamos parar de falação e ir pro que importa.

O código

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

import os
import sys
import resource


def create_daemon(stdout, stderr, working_dir):
    """
    cria um daemon
    """

    # faz o primeiro fork
    _fork_off_and_die()
    # cria uma nova sessão
    os.setsid()

    # faz o segundo fork
    _fork_off_and_die()

    # altera a máscara de arquivos
    os.umask(0)
    # altera o diretório de trabalho
    os.chdir(working_dir)
    # fecha todos os descritores de arquivos
    _close_file_descriptors()
    # redireciona stdout e stderr
    _redirect_file_descriptors(stdout, stderr)

def _fork_off_and_die():
    """
    cria um fork e sai do processo pai
    """
    pid = os.fork()
    # se o pid == 0, é o processo filho
    # se o pid > é o processo pai
    if pid != 0:
        sys.exit(0)

def _close_file_descriptors():

    # Fechando todos os file descriptors para evitar algum
    # lock
    # RLIMIT_NOFILE é o número de descritores de arquivo que
    # um processo pode manter aberto

    limit = resource.getrlimit(resource.RLIMIT_NOFILE)[1]

    for fd in range(limit):
        try:
            os.close(fd)
        except OSError:
            pass

def _redirect_file_descriptors(stdout, stderr):
    """
    redireciona stdout e stderr
    """

    # redirecionando stdout e stderr
    for fd in sys.stdout, sys.stderr:
        fd.flush()

    sys.stdout= open(stdout, 'a', 1)
    sys.stderr = open(stderr, 'a', 1)

def daemonize_func(func, stdout, stderr, working_dir, *args, **kwargs):
    """
    executa uma função como um daemon
    """
    create_daemon(stdout, stderr, working_dir)
    func(*args, **kwargs)

class daemonize(object):
    """
    decorator para executar uma função como daemon
    """

    def __init__(self, stdout='/dev/null/', stderr='/dev/null/',
                 working_dir='.'):

        # stdout e stderr são os lugares para onde serão redirecionados
        # sys.stdout e sys.stderr
        self.stdout = stdout
        self.stderr = stderr
        # working_dir é o diretório onde o daemon trabalhará
        self.working_dir = working_dir

    def __call__(self, func):
        def decorated_function(*args, **kwargs):
            daemonize_func(func, self.stdout, self.stderr, self.working_dir,
                      *args, **kwargs)

        return decorated_function

E agora você pode usar esse código assim:

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

import time
from daemonize import daemonize

@daemonize(stdout='log_b.txt', stderr='log_b.txt')
def b():
    print('comecando')
    time.sleep(10)
    print('fim')

if __name__ == '__main__':

    b()

Ou assim:

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

import time
from daemonize import daemonize_func

def a():
    print(time.time())
    time.sleep(50)
    print(time.time())

if __name__ == '__main__':
    daemonize_func(a, stdout='log_a.txt', stderr='log_a.txt', working_dir='.')

É isso, pessoal. Valeu. :)

[1] http://www.takeourword.com/TOW146/page4.html

[2] http://pubs.opengroup.org/onlinepubs/007904975/basedefs/xbd_chap11.html#tag_11_01_03


Manifestações, micaretações, os manifestantes mais-amor e os nacionalistas

  • publicado em 20 de julho de 2013

As manifestações contra o aumento das passagens organizadas pelo MPL começaram este ano em 06 de junho. Até o momento, dia 20 de junho, foram realizadas 6 manifestações o outra está marcada para hoje às 17:00 horas. Os quatro primeiros atos contaram com um número de participantes entre cinco mil no dia 06, e pouco mais de dez mil no dia 13. Os quinto e sexto atos, ocorridos nos dias 17 e 18 tiveram uma participação muito maior, com estimativas dando conta de mais de 100 mil pessoas. O que haveria mudado para que em quatro dias uma multidão que não está acostumada a participar de manifestações resolvesse passar a participar? Poderíamos dizer que o gigante acordou, mas isso não é explicação, é imagem da propaganda de uísque.

Os primeiros atos foram todos marcados por confrontos entre policiais e manifestantes. O ato do dia 13, já começou com um “clima” de escaramuça devido aos conflitos ocorridos no ato anterior, o maior até então, resultando em manifestantes presos e policiais feridos. As estratégias do governo do estado e da prefeitura eram a mesma, a mesma de sempre: vamos aumentar as passagens; vão reclamar; limpamos a bagunça e fim de papo. Mas no dia 13 o comando deixou a tropa solta demais, e o resultado foram mais confrontos e dessa vez com imagens muito ruins, com transeuntes e imprensa sendo feridos. Uma matilha solta e umas manchetes ruins fizeram o comando da polícia repensar suas táticas. Foram obrigados a segurar a tropa, e com isso uma nova maneira de limpar a bagunça deveria ser encontrada.

Já o Movimento Passe Livre estava numa curva crescente, com cada vez mais mobilização em suas manifestações de 2013. Com a repercussão da manifestação do dia 13, soube muito bem como canalizar a “publicidade favorável” em direção ao seu próximo ato, no dia 17, que seria o primeiro que teria uma micaretação com manifestantes mais-amor

A definição de micaretação é uma aglomeração de pessoas, que vão se manifestar politicamente, mas que no final das contas, estão mais interessadas em participar do ato em si, participar da festa, postar fotos na internet e coisas assim. O objetivo pode ser qualquer coisa: do caos instaurado no transporte coletivo à falta de amor em São Paulo passando pelo alto custo dos jogos de PS3. Na verdade, quem liga? Já o manifestante mais-amor, é o típico componente de uma micaretação. Ele fica bravo na internet, reclama dos políticos, do povo, do mundo todo e ultimamente estava meio entediado e andou vendo umas notícias de uns lugares lá longe e ficou com vontade de também ter sua própria manifestação-revolução (manifestação pacifico-revolucionaria-show-de-civilidade, claro).

Aqui entram os nacionalistas. Eles também estão buscando seus espaços e tentando espalhar suas ideias. E as mensagens deles são as mais palatáveis para o manifestante mais-amor. Eles falam sobre amor ao Brasil, volta da ordem e afins, e esse é um discurso muito difundido, o mais-amor já ouviu isso muitas vezes, quando ouve isso na sua micaretação, se encontra.

Com todos estes ingredientes, chegamos à manifestação do dia 17, onde houve a “mistura” de tudo isso: a crescente do movimento pela redução das passagens encabeçado pelo MPL, os manifestantes mais-amor empolgados com as histórias da internet e com toda a publicidade positiva veiculada nos meios de comunicação durante o fim de semana, e os nacionalistas e a direita em geral. O resultado foi uma manifestação realmente massiva, com a presença de dezenas de milhares de pessoas, mas por outro lado foi também uma manifestação desfocada, com a direita querendo impor sua própria agenda, despolitizada, com a maioria dos manifestantes sendo do tipo mais-amor, também tentando impor sua “agenda” despolitizada e com alguma ação radicalizada por parte dos manifestantes que se dirigiram ao palácio dos bandeirantes. A manifestação do dia 18 foi mais ou menos como está, mas as ações radicalizadas se dirigindo à sede da prefeitura. No dia 19, a prefeitura e o governo do estado anunciaram a revisão do aumento das tarifas do transporte nos ônibus, trens e metrô de São Paulo.

À primeira vista, temos uma vitória do movimento. E temos mesmo, a reivindicação inicial foi atendida, o preço das passagens baixou. Mas não custa olhar mais de perto. Primeiro: qual foi o papel da multidão de manifestantes mais-amor presentes nas duas últimas manifestações? Ele foi um papel decisivo, ou seja, uma participação sem a qual não haveria a conquista da reivindicação inicial? Neste ponto podemos olhar para os exemplos de outras cidades do país que tiveram o valor da tarifa do transporte reduzido por conta das manifestações. E nestas cidades não se viu toda essa “mobilização popular”, donde se conclui que este papel, seja ele qual for, não foi decisivo na obtenção do resultado obtido até agora.

Começa-se a vislumbrar o papel exercido pela massa de manifestantes mais-amor ao pensar-se em qual estratégia de contensão de danos poderia ser usada pelo governo do estado. Depois do dia 13, com a tropa na coleira, uma tática mais sutil teria que ser usada para “esvaziar” o movimento. Se você não pode bater e assustar para as pessoas não irem, nada melhor do que inflar em número e esvaziar no conteúdo político direto da manifestação. E assim foi feito, com a anuência dos meios de comunicação (desde que a classe que controla os meios de comunicação é a mesma que detém o controle do estado). Mas então, por que a tarifa foi reduzida? Porque a estratégia do governo do estado não foi tão bem sucedida. A manifestação realmente foi muito aumentada em número, na maioria dos casos, por manifestantes mais-amor, mas uma parcela dos manifestantes ainda continuou com o objetivo inicial, levando ao cabo protestos mais radicais e efetivos. Com isso, a tarifa teve que ser baixada.

Agora, sobram os questionamentos de qual será a herança e a continuação disto tudo. Aos militantes do MPL, cabe a tarefa de tentar fazer dos limões uma limonada, ou seja, conseguir canalizar pelo menos uma parte do apoio conquistado nos últimos dias para a questão central do MPL, que é o passe livre no transporte coletivo. À pessoas como eu, que estão de fora, cabe analisar como foi o funcionamento desta “manifestação espontânea” assim como analisar o papel da direita, principalmente dos nacionalistas, e em como eles tentarão captar o apoio popular.

Manifestações espontâneas, sem direção e coisas do tipo têm sua serventia e seu valor, mas também sofrem por serem muito fáceis de serem tomadas por elementos externos (no caso atual, nacionalistas e fascistas). E como os símbolos nacionalistas e as ideias fascistas são as ideias correntes, elas são muito facilmente assimiladas pelas massas. E aí reside um problema sério a ser enfrentado. A ascensão fascista na Europa no século XX teve como base, muitas vezes, o apoio popular, conseguido dessa maneira, com elementos fascistas se infiltrando em movimentos populares e cooptando os participantes menos conscienciosos com seus discursos de fácil assimilação. Percebendo-se disso, é preciso enfrentar esses problemas em todos os aspectos possíveis, desde o aspecto ideológico até o enfrentamento nas ruas e a quebra da ordem estabelecida. Os fascistas são bons nisso, os antifascistas precisam ser mais fortes e organizados que eles, ou fatalmente serão derrotados.

ATUALIZAÇÃO EM 21/06

O que era apenas uma probabilidade, agora está confirmada. Os fascistas conseguiram galgar seus espaços dentre os manifestantes mais-amor. Tanto é que a primeira linha da matilha fascista, os carecas e skin heads, estavam presentes ao ato, muito à vontade. E como esperado, os seus slogans nacionalistas caíram nas graças da massa. Agora já podemos dizer que o resquício destes atos, além da redução da tarifa dos transportes, foi, pelo menos em São Paulo, um maior contato e uma assimilação – talvez inadvertidamente – dos símbolos fascistas por parte da massa de manifestantes, os manifestantes mais-amor.

Agora o problema é muito maior, tem que se lutar para desconstruir estes símbolos fascistas no imaginário coletivo, ou o apoio aos fascistas, entre a população geral, pode aumentar. E temos exemplos de como isto termina.


Automatizando o deploy com fabric

  • publicado em 08 de janeiro de 2012

Fala, pessoal. Tudo certo? Hoje eu vim falar sobre o fabric. O fabric é um cara que te ajuda a automatizar o deploy permitindo que você execute comandos de shell na máquina local e (o que é mais legal) em um servidor remoto de maneira muito simples.

Como assim?

Imagine que você tem seu programa lá, bonitão, works on my machine certified, mas você precisa por isso em algum lugar acessível ao público. Você pode muito bem gerar um .tar do seu código, copiar pro servidor, instalar… enfim, fazer tudo o que precisa na mão. Nem é complicado. Mas fazer isso é chato, e se a coisa cresce, tem sempre o risco de esquecer algo. É aí que entra o fabric! Com ele, você escreve um script (em Python) pra fazer o seu deploy.

Tá, beleza. Mas como funciona?

É bem simples. A primeira coisa a se fazer é instalar o fabric e a maneira mais fácil de fazer isso é pelo gerenciador de pacotes do seu sistema operacional (seu s.o. tem gerenciador de pacotes, não?). Depois de instalado o fabric, é só você criar um arquivo chamado fabfile.py contendo os comandos necessários ao deploy.

Pra começar, vamos fazer um “olá” com o fabric pra gente ver como funciona. O fabfile pro nosso “ola” ficou assim:

#-*- coding: utf-8 -*-
# Arquivo fabfile.py

def ola(nome):
    print 'olá, ', nome

A sintaxe pra se executar o fabfile é a seguinte: fab <nome_da_funcao>:<arg1>, <arg2>, …

Então, pra executar nosso fabfile acima, executamos o seguinte comando:

$ fab ola:juca
ola,  juca

Done.

Tá, entendi. Agora um exemplo decente, vai.

Agora que já vimos como usar o fabric, vamos a um exemplo real, pra gente dar uma olhada em algumas coisas interessantes da api do fabric.

A idéia aqui vai ser a seguinte: Eu tenho um repositório git na minha máquina contendo o código que eu quero subir. O procedimento pra subir é gerar um .tar contendo o código de uma named tree qualquer (um branch, uma tag, um commit…), copiar esse tar pro servidor remoto, desempacotar o tar no servidor remoto, se já tiver uma versão mais antiga instalada, desinstalar essa versão antiga, instalar a versão nova e por fim, reiniciar o web server.

Tudo muito simples, mas ficar fazendo isso é muito chato, então o fabfile abaixo resolve isso pra gente:

import os
import time

# aqui importando uns caras legais da api do fabric
# local - roda um comando de shell na máquina local
# run - roda um comando de shell no servidor remoto
# put - faz uma cópia via ssh (scp) pro servidor remoto
# env - configurações do ambiente

from fabric.api import local, run, put, env


LOCAL_SRC_PATH = '/home/juca/mysrc/sourcecode2html'
LOCAL_BUILD_PATH = '/tmp/amazon-build'
REMOTE_SRC_PATH = '/home/deployuser/src/codeprettifier'

# aqui é uma string do tipo usuario@host[:porta]
# usuario é um usuário do sistema no servidor remoto.
# É uma string como o que você passa pro ssh
env.hosts = ['deployuser@myserver']

# É essa função que vai ser chamada na execução do fabfile, algo como:
# fab deploy:master
def deploy(tree_name):
    """ Executa as ações necessárias ao deploy
    """

    tar_file = _package_named_tree(tree_name)
    remote_tar_path, filename = _send_file(tar_file)
    _unpack_code(remote_tar_path, filename)
    _uninstall_last_version()
    _create_link_to_lastest(remote_tar_path)
    _build()
    _install()
    _restart_server()

def _package_named_tree(tree_name):
    """ cria um arquivo .tar.bz2 baseado numa named tree do git
    """
    try:
        os.mkdir(LOCAL_BUILD_PATH)
    except OSError:
        pass

    os.chdir(LOCAL_SRC_PATH)
    filename = '%s/codeprettifier-%s.tar.bz2' %(LOCAL_BUILD_PATH,
                                                tree_name)

    pack_command = 'git archive %s --prefix=codeprettifier/ |' % tree_name
    pack_command += ' bzip2 > %s' % filename

    # aqui, executando o comando na máquina local
    # com o local() da api do fabric
    local(pack_command)
    return filename

def _send_file(filename):
    """ send file to remote server
    """

    remote_tar_path = REMOTE_SRC_PATH + '/%s/' % int(time.time())
    try:
        # Aqui executando run(). O mkdir aí em baixo vai ser
        # executado no servidor remoto.
        # Assim que o primeiro run() é chamado, vai ser perguntada
        # a senha do usuário no host remoto.
        run('mkdir -p %s' % remote_tar_path)
    except:
        pass

    # aqui enviando arquivo via scp usando o put()
    # da api do fabric
    put(filename, remote_tar_path)
    filename = filename.split('/')[-1]
    return remote_tar_path, filename

def _unpack_code(remote_tar_path, filename):
    """ Desempacota o código no servidor remoto
    """
    run('cd %s' % remote_tar_path)
    run('tar -xjvf %s/%s -C %s' % (remote_tar_path, filename, remote_tar_path))

def _uninstall_last_version():
    """ unpacks the code on remote server
    """
    try:
        run('cd %s/latest/codeprettifier' % REMOTE_SRC_PATH)
    except:
        return

    uninstall_command = 'cd %s/latest/codeprettifier && ' % REMOTE_SRC_PATH
    uninstall_command += "sudo make uninstall | grep -v codeprettifier/ |"
    uninstall_command += 'grep -v Java/ | grep -v MultiLineStringDelimiter.pm |'
    uninstall_command += "cut -d'k' -f2 | grep -i CodePrettifier |"
    uninstall_command += " grep -v codeprettifier.pl |xargs sudo rm"

    try:
        run(uninstall_command)
    except:
        pass

    run('rm %s/latest' % REMOTE_SRC_PATH)

def _create_link_to_lastest(remote_tar_path):
    run('ln -s %s %s/latest' % (remote_tar_path, REMOTE_SRC_PATH))

def _build():
    """ Cria o Makefile pra instalação
    """
    remote_latest_dir = REMOTE_SRC_PATH + '/latest/codeprettifier'
    run('cd %s && perl Makefile.PL' % remote_latest_dir)

def _install():
    """ Faz a instalação em si
    """
    remote_latest_dir = REMOTE_SRC_PATH + '/latest/codeprettifier'
    run('cd %s && sudo make install' % remote_latest_dir)

def _restart_server():
    """ reinicia o server
    """
    run('sudo /sbin/service httpd restart')

Agora, é só executar

$ fab deploy:master

[deployuser@myserver] Executing task 'deploy'
[localhost] local: git archive master --prefix=codeprettifier/ | bzip2 > /tmp/amazon-build/codeprettifier-master.tar.bz2
[deployuser@myserver] run: mkdir -p /home/deployer/src/codeprettifier/1326011668/
[deployuser@myserver] Login password:
[deployuser@myserver] put: /tmp/amazon-build/codeprettifier-master.tar.bz2 -> /home/deployer/src/codeprettifier/1326011668/codeprettifier-master.tar.bz2
[deployuser@myserver] run: cd /home/deployuser/src/codeprettifier/1326011668/
[deployuser@myserver] run: tar -xjvf /home/deployuser/src/codeprettifier/1326011668//codeprettifier-master.tar.bz2 -C /home/deployuser/src/codeprettifier/1326011668/
...

[deployuser@myserver] run: cd /home/deployuser/src/codeprettifier/latest/codeprettifier && sudo make uninstall | grep -v codeprettifier/ |grep -v Java/ | grep -v MultiLineStringDelimiter.pm |cut -d'k' -f2 | grep -i CodePrettifier | grep -v codeprettifier.pl |xargs sudo rm
[deployuser@myserver] run: rm /home/deployuser/src/codeprettifier/latest
[deployuser@myserver] run: ln -s /home/deployuser/src/codeprettifier/1326011668/ /home/deployuser/src/codeprettifier/latest
[deployuser@myserver] run: cd /home/deployuser/src/codeprettifier/latest/codeprettifier && perl Makefile.PL
...

[deployuser@myserver] run: cd /home/deployuser/src/codeprettifier/latest/codeprettifier && sudo make install
...

[deployuser@myserver] run: sudo /sbin/service httpd restart
...

Done.
Disconnecting from deployuser@myserver... done.

E pronto, seu deploy foi feito automaticamente!

Pra finalizar, quero dizer que isso foi só um exemplo, você pode escrever o procedimento de deploy que quiser com o fabric. Ele é bem versátil!

Bom, é isso pessoal. Até a próxima! :)


Voltando a falar no git

  • publicado em 02 de janeiro de 2012

Boas pessoal, essa aqui é bem rápida:

Acabei de reler um post que escrevi a um tempo atrás. O post fala como usar o git com http. O negócio é zuado. Funciona, mas não é o jeito certo de fazer a coisa.

O melhor é usar o gitorious, e você ainda pode baixar o código e ter a sua própria instalação.

Simples assim.


Poo no Perl!

  • publicado em 28 de dezembro de 2011

Boas pessoal. Tô de volta. :) Depois de um bom tempo sem postar nada, vou aproveitar minha semaninha de folga pra tirar as teias do porão e vamos falar um pouquinho sobre orientação a objetos no Perl.

Mas o Perl é um cara sem classe, não?

No Perl realmente não existe uma palavra mágica class ou algo do tipo, mas a gente pode criar classes sim!

As classes no Perl são criadas o mecanismo de package que também é usado mais usualmente para criar módulos tradicionais. E os objetos… bom, os objetos são referências abençoadas ! :P

Referência abençoada? Que diabo é isso?

Calma… nada de misticismos. Uma referência abençoada é só uma referência que foi passada como argumento para a função bless. É essa função a responsável por dizer: “essa referência pertence a este package (classe)”. Depois de abençoada, você será capaz de chamar funções através dessa referência, ou, em outras palavras, você terá métodos!

Entendi, mas já cansei de papo…

Beleza, já falei bastente, e agora é hora do código.

Pra este exemplo vou criar uma classe (package) Pessoa (como um módulo normal do Perl, isso será gravado em um arquivo chamado Pessoa.pm), e nessa classe criarei o contrutor da classe e os seus outros métodos. Agora sim, o código:

package Pessoa; # Arquivo Pessoa.pm

use strict;
use warnings;

# Aqui é o contrutor da classe.
# O nome 'new' não é uma obrigação, só
# uma convenção de uso.
sub new{
    # classe onde o a referência será abençoada.
    # Isso aqui não precisa ser passado como parâmetro
    # na hora de instanciar o objeto, é passado implicitamente
    my $class = shift;

    # Nossa referência que será abençoada.
    # Pode ser uma referência qualquer, não necessáriamente
    # uma referência a um hash;
    my $self = {};
    $self->{NOME} = undef;
    $self->{IDADE} = undef;

    # Agora a mágica acontece
    bless($self, $class);

    return $self;
}

# Métodos pra proteger o acesso aos nossos atributos
sub nome{
    # Como $class no contrutor, $self aqui também é parâmetro implícito
    my $self = shift;
    $self->{NOME} = shift if @_;
    return $self->{NOME};
}

sub idade{
    my $self = shift;
    $self->{IDADE} = shift if @_;
    return $self->{IDADE};
}

# Outro metodozinho só de exemplo
sub fale{
    my $self = shift;
    my $fala = shift || "Qualquer coisa";
    return $fala
}

# Precisa retornar um valor verdadeiro
1;

Agora, vamos criar uma subclasse de Pessoa

package PessoaMuda; # Arquivo PessoaMuda.pm

# Importando a super classe
use Pessoa;

# Aqui estamos dizendo que PessoaMuda é uma Pessoa
@ISA = (Pessoa);

# Sobrescrevendo o método fale pra que retorne nada...
sub fale{
    my $self = shift;
    return
}

1;

E, por fim, vamos usar nossos objetos

#!/usr/bin/env perl

use strict;
use warnings;

# Importando nossas classes
use Pessoa;
use PessoaMuda;

# Nova instância de Pessoa;
my $p = Pessoa->new();
$p->nome('Alguém');
print $p->nome();

$p->idade(10000);
print $p->idade();

print $p->fale('Eu nasci a 10000 anos atrás...') . "\n";

# Uma pessoa muda
my $pm = PessoaMuda->new();
$pm->nome('Ninguém');
print $pm->nome();

$pm->idade(10000);
print $pm->idade();

# Mudos não falam, lembra? Sobrescrevemos o método...
print $pm->fale('Alguma coisa');

Bom, é isso! Pra mais sobre orientação a objetos no Perl, leiam o perltoot.

Valeu, e até a próxima!


Dissecando um inseto (ou como usar o pdb, the Python Debugger)

  • publicado em 16 de agosto de 2010

Boas, pessoal! Hoje eu estou aqui pra mostrar como se usa o (básico do) Python Debugger, o pdb, que é (óbvio) um depurador pra se usar com o Python. Este exemplo é feito usando o Python 3.

Mas… como é?

Bom, existe mais de uma maneira de usar o pdb. A que vou mostrar é chamando o pdb como um script para depurar outro script. Então, primeiro, vamos criar um scriptosco com um erro mais tosco ainda pra gente começar a brincar com o pdb. O scriptosco é o seguinte:

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

def divide(a, b):
    erro = False
    try:
        return float(a) / float(b)
    except:
        erro = True

    if erro:
        raise Exception("Não vou te dizer qual é o erro...")

a = 1
b = 2
divide(a, b)
a = 3
b = 0
divide(a, b)

Bom, rodando isso aí, a gente tem o seguinte:

$ $python3 exemplopdb.py
Traceback (most recent call last):
  File "exemplopdb.py", line 18, in
    divide(a, b)
  File "exemplopdb.py", line 11, in divide
    raise Exception("Não vou te dizer qual é o erro...")
Exception: Não vou te dizer qual é o erro...

Será que esse tal de pdb funciona mesmo?

Agora que temos um erro, podemos usar o pdb pra achar cara. A gente vai fingir que não vê nada errado até a hora certa, tá? :P A sintexe pra se chamar o pdb é a seguinte: python -m pdb <meu_script> Então, ao chamar nosso doente junto com o pdb, a gente vai cair no shell do pdb. Assim:

$ python3 -m pdb exemplopdb.py
--Return--
> /media/5511fc83-ad85-484b-9b0e-0948abcb6026_/virtualpython/lib/python3.1/encodings/__init__.py(67)normalize_encoding()->'utf_32_be'
-> return ''.join(chars)
(Pdb)

Nós começaremos criando breakpoints nas linhas 11 e 18. Fazemos isto com o comando b[reak]. Depois, utilizando o comando c[ontinue], continuaremos a execução do programa até que algum breakpoint seja encontrado. E por fim, usaremos o comando list para listar um trecho de código e ver onde paramos.

(Pdb) break exemplopdb.py:11
Breakpoint 1 at /media/sda6/Projetos & afins/scripts/exemplopdb.py:11
(Pdb) b exemplopdb.py:18
Breakpoint 2 at /media/sda6/Projetos & afins/scripts/exemplopdb.py:18
(Pdb) c
> /media/sda6/Projetos & afins/scripts/exemplopdb.py(18)()
-> divide(a, b)
(Pdb) list
 13          a = 1
 14          b = 2
 15          divide(a, b)
 16          a = 3
 17          b = 0
 18 B->      divide(a, b)
 19
[EOF]
(Pdb)

Nosso primeiro breakpoint é bem na chamada da função, então vamos «entrar na função» e acompanhar a execução. A gente faz isso com o comando s[tep]. Uma vez na função, a gente pode acompanhar a execução linha-por-linha usando o comando n[ext] e também pode imprimir os valores das variáveis com print.

(Pdb) s
--Call--
> /media/sda6/Projetos & afins/scripts/exemplopdb.py(3)divide()
-> def divide(a, b):
(Pdb) list
  1          #-*- coding: utf-8 -*-
  2
  3  ->      def divide(a, b):
  4              erro = False
  5              try:
  6                  return float(a) / float(b)
  7              except:
  8                  erro = True
  9
 10              if erro:
 11 B                raise Exception("Não vou te dizer qual é o erro...")
(Pdb) n
> /media/sda6/Projetos & afins/scripts/exemplopdb.py(4)divide()
-> erro = False
(Pdb) n
> /media/sda6/Projetos & afins/scripts/exemplopdb.py(5)divide()
-> try:
(Pdb) n
> /media/sda6/Projetos & afins/scripts/exemplopdb.py(6)divide()
-> return float(a) / float(b)
(Pdb) print(a, b)
(3, 0)
(Pdb) n
ZeroDivisionError: 'float division'
> /media/sda6/Projetos & afins/scripts/exemplopdb.py(6)divide()
-> return float(a) / float(b)
(Pdb)

Bom, aí matamos o erro, não? Funciona mesmo! Moral da história: As vezes o pdb pode salvar várias horas do seu dia. Faça dele um amigo. :)


Liberando espaço no HD com localepurge

  • publicado em 07 de julho de 2010

Bom, faz tempo que eu não venho aqui, então aqui vai uma rapidinha só pra tirar as teias-de-aranha. Esse programinha aí, o localepurge, é um cara que apaga as traduções de programas e manuais que estão nos idiomas que você não usa. Dependendo do tanto de coisa que você tem instalado, vai ter bastante espaço liberado. Isso me libera pra mais de 500 MiB. Pra instalar no Debian, como sempre, é simples.

# aptitude install localepurge

E depois:

# localepurge

É isso.


Corrigir erro “Network Unreachable” com Java no Debian

  • publicado em 04 de março de 2010

Puta, perdi uma tarde inteira quebrando a cabeça com isso. Estava tentando usar o Squirrel-SQL (e o Sql Developer também), mas sempre dava merda, com o java dizendo que «Network Unreachble». Depois de bastante tempo, esse cara me deu a resposta. O que acontece é que no arquivo /etc/sysctl.d/bindv6only.conf a variável net.ipv6.bindv6only estava marcada como 1, e essa é a fonte do problema. Esta variável tem que estar marcada como 0. Pra arrumar isso, use:

# sed -i 's/net.ipv6.bindv6only\ =\ 1/net.ipv6.bindv6only\ =\ 0/' /etc/sysctl.d/bindv6only.conf
# invoke-rc.d procps restart

É isso aí.


Uma pincelada no GIT parte II - Configurando HTTP

  • publicado em 08 de janeiro de 2010

NÃO LEIA ISSO - ou melhor, pode até ler, mas esse aqui não é o jeito certo de fazer as coisas. :P Leia esse aqui

Antes de mais nada, a fonte primária de informação é essa aqui. Isto aqui é a continuação do post anterior sobre o git. Como prometido, hoje vou explicar como configurar o git para usar http ou invés de ssh.

Começando do começo

Bom, começarmos a configurar o servidor você vai precisar de algumas coisas instaladas nele. São elas:

  • Um servidor web apache

  • Git

Você vai precisar também ter acesso como root neste servidor para poder fazer a configuração inicial do repositório. Antes de prosseguir, vale uma ressalva: O apache é um treco chato… Se você pegar os fontes e compilar, o “document root” e os arquivos de configuração serão em um lugar. Se você estiver usando um Red Hat serão em outro, em um Debian serão em outro ainda. E no windows então! Nem faço idéia como funciona o apache no windows.

Por isso, primeiro sempre darei uma explicação «genérica» de como a coisa funciona. Nos exemplos que darei, os caminhos de arquivos e tudo mais serão baseados em Debian. Se você uma um sistema operacional diferente, leia o manual do apache e veja onde estão localizados seus arquivos.

Configurando o servidor

A primeira coisa a fazer é certificar-se que o módulo dav_module está sendo carregado pelo apache. Isto pode ser feito adicionando as seguitnes linhas os seu httpd.conf:

LoadModule dav_module libexec/httpd/libdav.so
AddModule mod_dav.c
DAVLockDB "/usr/local/apache2/temp/DAV.lock"

No Debian, isto pode ser feito de uma outra maneira. Deste jeito:

# a2enmod dav_fs
# a2enmod dav

Depois disto, é preciso criar um repositório «pelado» para o git. Para isto, criaremos um diretório dentro do nosso «document root», criaremos o reporitório dentro deste diretório recém criado, e por fim, deixaremos o usuário do apache como dono deste diretório e de todos os seus sub-diretórios. No Debian fica assim:

# mkdir /var/www/novo_projeto.git
# cd /var/www/novo_projeto.git
# git --bare init
# cd ../
# chown -R www-data:www-data novo_projeto.git/

Agora, vamos criar um usuário/senha para acessar o projeto . A sintaxe é assim: htpasswd -c /caminho/para/o/arquivo/de/senha <usuario>.

O diretório onde se encontra o arquivo de senhas tem que ser lido pelo apache e, de preferência, não ser lido pelo resto do mundo. O parâmetro -c passado ao htpasswd significa que criaremos um novo arquivo. Se o arquivo já exitir e você quiser apenas acrescentar mais um usuário, htpasswd deve ser chamado sem o parâmetro -c. Aqui no Debian, ficou assim:

# htpasswd -c /etc/apache2/passwd.git gituser

Depois de criado o usuário, vamos definir as regras do apache para o diretório do nosso repositório. Geralmente você precisaria adicionar algo assim ao seu httpd.conf:

<Location /meu_repo>
  DAV on    AuthType Basic
  AuthName "Git"
  AuthUserFile /caminho/para/o/arquivo/de/senha
  Require valid-user
</Location>

Como o Debian lê automaticamente os arquivos em /etc/apache2/conf.d/, eu criei o arquivo /etc/apache2/conf.d/git.conf e adicionei o seguite a ele:

<Location /novo_projeto.git>
  DAV on    AuthType Basic
  AuthName "Git"
  AuthUserFile /etc/apache2/passwd.git
  Require valid-user
</Location>

Agora, reinicie o apache. No Debian fica assim: # /etc/init.d/apache2 restart Neste ponto o servidor já deve estar funcionando corretamente. Para testar, acesse o seu servidor da seguinte maneira: http://servidor/novo_projeto.git

O servidor peguntará seu usuário/senha. Depois de informá-las, se você ver uma listagem de diretórios e arquivos do git, o servidor está funcionando corretamente.

Configurando o cliente

Agora que o servidor já está configurado, é hora de configurar o cliente. A primeira coisa a fazer, é informar nosso usuário e senha pra que não tenhamos que ficar digitando isso toda vez… é chato! Fazemos isso adicionando as seguintes linhas ao arquivo $HOME/.netrc:

machine <servidor> login <usuário> password <senha>

Como nossa senha está aí, é bom restringir o acesso a este arquivo. Fazemos isto assim:

$ chmod 600 ~/.netrc

Depois disso, precisamos configurar o git para acessar nosso servidor. Isso é muito simples.

$ git config remote.novo_projeto.url http://<usuário>@<servidor>/novo_projeto.git/

OBS: Não esqueça da “/” no final da url, senão você vai cair num redirecionamento eterno…

Bom, aqui já está tudo configurado. Só o que precisamos fazer agora é “empurrar” os arquivos do projeto para o servidor.

$ git push novo_projeto master

Fetching remote heads...
refs/ refs/heads/ refs/tags/
updating 'refs/heads/master' from 5b0bc00758855aef6dafe7aa9849443aea0dbf1c to b4857d97182a582c76961370de489b14385f9af9
sending 13 objects done

Bom, é isso aí! Git configurado pra usar HTTP. Agora você que decide como usar seu Git.


Anterior Próximo