RapydScript, le JavaScript qui se déguise en Python

Posté par  . Édité par Nils Ratusznik, palm123, Nÿco, bubar🦥, Ontologia et ZeroHeure. Modéré par ZeroHeure. Licence CC By‑SA.
Étiquettes :
28
29
avr.
2015
JavaScript

RapydScript est un langage qui se compile en JavaScript, avec une syntaxe et des fonctionnalités qui se veulent proches de Python. Pour ceux qui connaissent coffeeScript, RapydScript est pareil mais inspiré par la lisibilité de Python. Il ne s'agit pas d'une nouvelle tentative de faire tourner Python dans le navigateur, comme par exemple Pyjamas. RapydScript reste du JavaScript, il n'introduit aucune limitation ni surcoût. Il paraît juste plus propre et donne l'impression d'écrire du Python.

Ses quelques fonctionnalités :

  • comme CoffeeScript, RapydScript répare les incohérences et inconsistances de JavaScript ;
  • un système de classes similaire à Python ;
  • support des listes en compréhensions ;
  • des exceptions comme en Python ;
  • des modules, plus flexibles que les modules Python ;
  • des arguments optionnels pour les fonctions, comme en Python ;
  • des décorateurs (sans arguments) ;
  • un système d'héritage plus puissant que celui de Python et plus clair que celui de JavaScript ;
  • le support d'objets (dictionnaires) avec fonctions anonymes, comme en JavaScript ;
  • la possibilité d’appeler n'importe quelle fonction, méthode, code JavaScript ou appel au DOM de n'importe quel framework, sans syntaxe spéciale ;
  • le choix entre les méthodes et fonctions à la Python ou leurs équivalents JavaScript ;
  • RapydScript s'auto-compile, c'est à dire que le compilateur est lui-même écrit en RapydScript et compile en JavaScript.

Sommaire

Voici un court exemple qui utilise JQuery :

class Popup:
    def __init__(self, containterId):
        # pop-up class will give us absolute position, high z-index and center the container
        self.$popup = $('#' + containerId).addClass('pop-up').mouseout(def():
            self.hide() # unlike 'this', 'self' will reference the object itself
        )
        self._$darkbg = $('#dark-bg') # overlays background with a dim layer to draw focus to the pop-up
    def show(self):
        self._$darkbg.show()
        self.$popup.show()
    def hide(self):
        self._$darkbg.hide()
        self.$popup.hide()

msg = Popup('my-popup')
msg.show()

Notons toutefois quelques limitations :

  • le source mapping n'est pas implémenté, mais l'auteur compte le faire ;
  • pas shell (repl) non plus, mais l'auteur le souhaite également ;
  • RapydScript a quelques bugs connus.

Installation

Comme d'habitude avec les applications Node.js, on utilise npm :

npm install rapydscript -g

Bibliothèque standard

Il est assez facile de faire tourner un code Python existant dans le navigateur car RapydScript a ré-implenté quelques bibliothèques standard de Python. Par exemple si vous utilisez range, print, ou list.append, il vous suffit d'importer la stdlib.js de rapydscript. Soit par un import explicite dans votre HTML, soit par la ligne import stlib dans votre code .pyj.

Il existe aussi le module math, re, unittest, random et yql.

Mais si vous écrivez du nouveau code, il est conseillé de se baser sur les bibliothèques JavaScript existantes, plus fournies.

Exemples

Appel à JavaScript

Si jamais vous devez appeler directement du JavaScript à cause d'une limitation de RapydScript, vous pouvez le faire avec la fonction JS :

JS('module').exports # meilleur équivalent à JS('module.exports')

Définir des fonctions

Une fonction tout à fait classique en RapydScript :

#regular
def fibonacci(n):
    if n == 0: return 0
    elif n == 1: return 1
    else: return fibinacci(n-1) + fibinacci(n-2)
a = fibinacci(8)

L'équivalent en JavaScript :

function fibonacci(n) {
    if (n === 0) {
        return 0;
    } else if (n === 1) {
        return 1;
    } else {
        return fibinacci(n - 1) + fibinacci(n - 2);
    }
}
a = fibonacci(8);

Utiliser des fonctions anonymes :

# anonymous
math = {
    add:    def(a, b): return a+b;,
    sub:    def(a, b): return a-b;,
    mul:    def(a, b): return a*b;,
    div:    def(a, b): return a/b;,
    roots:  def(a, b, c):
        r = Math.sqrt(b*b - 4*a*c)
        d = 2*a
        return (-b + r)/d, (-b - r)/d
}

Des arguments optionnels comme en Python :

# with optional arguments
def get(item, quantity=1):
return [item for i in range(quantity)]
a = get('hammer')
b = get('nail', 3)

Ce qui produit le JavaScript suivant :

function get(item, quantity) {
    if (typeof quantity === "undefined") quantity = 1;
    return (function() {
        var _$rapyd$_Iter = range(quantity), _$rapyd$_Result = [], i;
        for (var _$rapyd$_Index = 0; _$rapyd$_Index < _$rapyd$_Iter.length; _$rapyd$_Index++) {
            i = _$rapyd$_Iter[_$rapyd$_Index];
            _$rapyd$_Result.push(item);
        }
        return _$rapyd$_Result;
    })();
}
a = get("hammer");
b = get("nail", 3);

// les deux fonctions utilitaires créées:
function range(start, stop, step) {
    if (arguments.length <= 1) {
        stop = start || 0;
        start = 0;
    }
    step = arguments[2] || 1;
    var length = Math.max (Math.ceil ((stop - start) / step) , 0);
    var idx = 0;
    var range = new Array(length);
    while (idx < length) {
        range[idx++] = start;
        start += step;
    }
    return range;
}
function _$rapyd$_print() {
    var args, output;
    args = [].slice.call(arguments, 0);
    output = JSON.stringify(args);
    if ("console" in window) console.log(output.substr(1, output.length-2));
}

L'usage de décorateurs (sans arguments) :

# and finally, decorated
def makebold(fn):
    return def():
        return "<b>" + fn() + "</b>"

def makeitalic(fn):
    return def():
        return "<i>" + fn() + "</i>"

@makebold
@makeitalic
def hello():
    return "hello world"

Example: écrire une chaîne de callbacks

On fait souvent appel à une suite de callbacks en JavaScript. C'est tout à fait facile en RapydScript :

params = {
    width:  50,
    height: 30,
    onclick:    def(event):
        alert("you clicked me"),
    onmouseover:    def(event):
        $(this).css('background', 'red')
    ,
    onmouseout: def(event):
        # reset the background
        $(this).css('background', '')
}

Pour plus de détails, voyez "chaining blocks" dans la documentation.

Autres outils

RapydScript est assez jeune et n'a pas autant d'outils que, par exemple, CoffeeScript, mais on commence à voir des extensions pour Django ou d'autres outils Node, comme Gulp ou Grunt, ce qui permet de l'intégrer facilement à son projet AngularJS ou autre.

un dé-compileur JavaScript->RapydScript :

https://github.com/charleslaw/RapydScript-Decompiler

RapydFiddle pour partager du code en ligne

http://rapydscript.pyjeon.com/ (cliquer sur "démo")

gulp

https://github.com/vindarel/gulp-rapyd (utilisable mais pas fini)

grunt

https://github.com/loolmeh/grunt-rapydscript

extension pour Django

https://github.com/pztrick/DjScript

adaptation à Django-pipeline

https://github.com/traverseda/django-pipeline-rapydscript

mode emacs

https://gitlab.com/emacs-stuff/rapyd-mode

vim

https://github.com/atsepkov/vim-rapydscript (la configuration de l'auteur)

atom

https://atom.io/packages/language-rapydscript

Conclusion

Nous n'avons pas expliqué tout le fonctionnement de RapydScript, pour cela il y a la bonne documentation. Nous espérons vous avoir fait miroiter de nouveau ce vieux rêve: écrire une application web dans un seul langage, en Python.

Aller plus loin

  • # Pyjamas

    Posté par  (site web personnel, Mastodon) . Évalué à 10.

    Pyjamas ne fait pas tourner Python dans le navigateur, il transpile Python en Javascript, d'une part, et fourni une suite logicielle pour développer une application web comme une application de bureau d'autre part. Il est tout à fait possible d'appeler du Javascript avec Pyjamas via JS() également, rapydscript s'en inspire certainement.

    C'est très différent de quelque chose comme Brython ou PyPy.js qui eux fournissent un interprète Python en Javascript, donnant une bien meilleure compatibilité au prix d'un chargement long et d'une lourdeur générale.

    Pyjamas était un projet très intéressant, malheureusement il a plusieurs problèmes, en particulier:

    • quelques défauts de conceptions comme la détection des fonctionnalités à partir du navigateur, probablement héritée de GWT

    • il est et restera très certainement bloqué sur Python 2.7

    • il a été enlevé de Debian car pyjamas-desktop (qui permettait d'utiliser une appli Pyjamas sans navigateur, en Python natif) ne compilait plus.

    • et surtout il n'est plus maintenu suite à un détournement (pas fork, « détournement ») de projet des plus lamentables, cf mon commentaire qui expliquait les grandes lignes

    Nous utilisons Pyjamas pour l'interface web de Salut à Toi, et nous avons essayé de le faire revenir dans Debian en contactant l'auteur et l'ancien mainteneur, en supprimant la partie pyjamas-desktop, et en essayant de bouger un peu tout le monde. Le paquet est reparti, mais il a été refusé à cause d'un problème de vérification de licences, et nous n'avons pas eu le temps de relancer la machine, surtout qu'on avait un peu l'impression de se battre contre des moulins à vent, le dév principal semblant débordé. Bref, on perd très bêtement un super projet libre.

    Du coup nous avons cherché (et cherchons toujours) des alternatives, et on a vu Rapydscript, mais après une rapide recherche ça ne gère pas Python aussi bien qu'un Pyjamas et ça n'est pas le but si j'ai bien compris (le but n'est pas vraiment de transpiler Python en JS, mais d'offrir une syntaxe « à la » Python, j'ai bon ?). Autrement dit, ça ne peut pas nous convernir a priori comme alternative…

    • [^] # Re: Pyjamas

      Posté par  . Évalué à 1.

      o____O truc de fou !
      Merci pour les info.

    • [^] # Re: Pyjamas

      Posté par  . Évalué à 2.

      Tu as bon.
      Les mots de l'auteur: "To those familiar with Pyjamas, RapydScript brings many of the same features and support for Python syntax without the same overhead or need to think in GWT."

      • [^] # Re: Pyjamas

        Posté par  . Évalué à 1.

        Du coup c'est justement la grosse limitation de Pyjamas n'est-ce pas: on écrit du GWT, on n'écrit pas du "javascript" pour n'importe quel framework, que ce soit Angular/Mithril/Ember etc. J'ai bon ?

        • [^] # Re: Pyjamas

          Posté par  (site web personnel, Mastodon) . Évalué à 2.

          On n'est pas obligé d'utiliser leur framework, et d'ailleurs je regrette un peu qu'on l'ait fait parce que si j'avais à refaire ça moi même (ce qui va finir par arriver si on ne trouve pas de bonne alternative), je préférerais être au plus proches du HTML 5.

          Le framework à la GWT essaye de masquer le HTML au maximum à coup de tables, et ça fait des pages pas super.

          En fait quand il parle « d'overhead » dans rapydscript, il parle surtout (de ce que j'avais compris quand j'avais regardé) du fait que pyjamas cherche la compatibilité Python au maximum, jusqu'aux exceptions qui sont comme en python (super pratique au passage), et c'est parfois au prix de constructions lourdes en javascript. Bon après c'est des options de compilation qui s'activent ou pas, on peut faire un code qui compile en un javascript simple (mais moins optimisé) par exemple.

          Le truc aussi c'est que javascript évolue: j'ai assisté à la conf Mozilla sur son futur au dernier Fosdem, et y'a de plus en plus de constructions similaires à ce que fait Python (bon avec une syntaxe dégueulasse), du coup je pense que si on part directement sur Python 3/ECMAscript 6 ou 7, qu'on fait table rase du passé (genre I.E. 6 qui est géré par pyjamas) on peut arriver à un truc potable sans trop de contorsions. En plus maintenant il est possible de lier une ligne de code d'un langage source (donc Python ici) à son équivalent transpilé en Javascript. Bref un Pyjamas aujourd'hui sans l'héritage de GWT serait une solution excellente à mon avis.

          En plus Mozilla fourni un outil pour traduire un javascript moderne en l'équivalent plus ancien, pour une compatibilité avec les vieux navigateurs.

          Bref, j'aimerais bien avoir le temps de partir dans cette voie.

  • # ES6

    Posté par  (site web personnel) . Évalué à 4.

    Le premier exemple est un peu présenté comme si la version JS était plus longue à écrire alors que le code présenté est le code généré par RapydScript. En écrivant les deux versions à la main, on arrive à quelque chose de beaucoup plus proche.

    def fibonacci(n):
        if n == 0: return 0
        elif n == 1: return 1
        else: return fibinacci(n-1) + fibinacci(n-2)
    a = fibinacci(8)
    function fibonacci(n) {
        if (n === 0) return 0;
        else if (n === 1) return 1;
        else return fibinacci(n-1) + fibinacci(n-2);
    }
    a = fibonacci(8);

    Pour les autres exemples, il ne faut pas oublier que le JS est aussi un langage qui évolue et que beaucoup des fonctionnalités de python sont disponibles aussi en JS.

    math = {
        add:    (a, b) => a+b,
        sub:    (a, b) => a-b,
        mul:    (a, b) => a*b,
        div:    (a, b) => a/b,
        roots:  (a, b, c) => {
            var r = Math.sqrt(b*b - 4*a*c),
                d = 2*a;
            return [(-b + r)/d, (-b - r)/d];
        }
    }
    var [one, two] = math.roots(2, 5, 2);
    function get(item, quantity=1) {
        return [for (i of new Array(quantity)) item]
    }
    var a = get('hammer');
    var b = get('nail', 3);

    Les deux exemples si dessus marchent avec la dernière version de Firefox. Pour les autres navigateurs http://babeljs.io/ transforme ça en vieux JS.

    Les décorateurs sont aussi en préparation pour la version 7 mais je n'ai pas encore eu l'occasion de jouer avec. https://github.com/wycats/javascript-decorators

    L'intérêt de suivre un standard, c'est d'avoir plus de garanties sur son évolution. Le code JS écrit il y a 20 ans tourne toujours aujourd'hui et je ne vois pas ça changer pour les 20 prochaines années. Je suis pas vraiment sur de pouvoir dire la même chose de CoffeeScript, TypeScript, RapydScript…

    • [^] # Re: ES6

      Posté par  . Évalué à 3.

      C'est juste. Mais l'avantage direct vendu par Coffee, Rapyd et consorts est de réparer des incohérences de javascript qu'il faut absolument connaître, sinon c'est galère et bugs (du genre == et === qui retournent des résultats surprenants), en plus d'offrir une syntaxe plus ergonomique. Tu trouves pas que ce sont de gros gains ?

      • [^] # Re: ES6

        Posté par  . Évalué à 1.

        Et aussi de pousser discrètement pour l'inclusion de Python comme langage de script pour les navigateurs, ce qui pour le coup serait la fin des problèmes.

        • [^] # Re: ES6

          Posté par  . Évalué à 2.

          L'indentation qui sert à délimiter les blocs de code, moi ça me pose plein de problèmes… (merci la guerre entre les espaces et les tabulations).

          (j'ai pas dit que j'étais contre l'indentation, au contraire)

          "Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)

          • [^] # Re: ES6

            Posté par  (site web personnel) . Évalué à 3.

            Il suffit de faire comme tout le monde et de suivre la PEP008 (qui décrit une norme de code à suivre), et tu n'as alors aucun problème.
            Globalement, la PEP008 est très suivie, la plupart des codes Python que j'ai pu relire s'y conforment. Du coup, on a un écosystème assez homogène à ce niveau là et il est facile de se plonger dans le code d'un autre.

            • [^] # Re: ES6

              Posté par  . Évalué à 3.

              C'est juste dommage que la pep8 décrive des règles moches.

              « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

            • [^] # Re: ES6

              Posté par  . Évalué à 4.

              Le truc, c’est que le principal souci de la structuration par indentation, c’est que c’est fragile :

              if machin:
                  truc()
                  bidule()
              autretruc()
              

              C’est assez facile, lors de copier / coller malencontreux, de refactorisation de code (par exemple, en copiant collant quelque chose entre bidule et autretruc) de péter involontairement le bloc.

              En tout cas personnellement c’est un problème que j’ai rencontré à plusieurs reprises. Il y a peut-être des techniques pour éviter cela, mais moi ça me fait préférer le fait d’utiliser des blocs bien délimités avec terminateur de bloc.

              Mes commentaires sont en wtfpl. Une licence sur les commentaires, sérieux ? o_0

              • [^] # Re: ES6

                Posté par  (site web personnel) . Évalué à 1.

                Je ne sais pas comment je fais, mais ça ne m'est jamais arrivé de rajouter des bugs comme ça. Peut-être que mon IDE y est pour beaucoup, mais ça ne m'a jamais dérangé.

                • [^] # Re: ES6

                  Posté par  . Évalué à 5.

                  Moi, ça m'est déjà arrivé mais pas avec des copier-coller mais avec des git merge.

                  « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.