• Stars
    star
    306
  • Rank 131,407 (Top 3 %)
  • Language
    Python
  • Created about 7 years ago
  • Updated about 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Python to python compiler that allows you to use Python 3.6 features in older versions.

Py-backwards Build Status

Python to python compiler that allows you to use some Python 3.6 features in older versions, you can try it in the online demo.

Requires Python 3.3+ to run, can compile down to 2.7.

Supported features

Target 3.5:

Target 3.4:

Target 3.3:

Target 3.2:

Target 2.7:

For example, if you have some python 3.6 code, like:

def returning_range(x: int):
    yield from range(x)
    return x


def x_printer(x):
    val: int
    val = yield from returning_range(x)
    print(f'val {val}')


def formatter(x: int) -> dict:
    items: list = [*x_printer(x), x]
    print(*items, *items)
    return {'items': items}


result = {'x': 10, **formatter(10)}
print(result)


class NumberManager:
    def ten(self):
        return 10

    @classmethod
    def eleven(cls):
        return 11


class ImportantNumberManager(NumberManager):
    def ten(self):
        return super().ten()

    @classmethod
    def eleven(cls):
        return super().eleven()


print(ImportantNumberManager().ten())
print(ImportantNumberManager.eleven())

You can compile it for python 2.7 with:

➜ py-backwards -i input.py -o output.py -t 2.7

Got some ugly code and ensure that it works:

➜ python3.6 input.py
val 10
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10
{'x': 10, 'items': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}
10
11
➜ python2 output.py                           
val 10
0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10
{'x': 10, 'items': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}
10
11

Usage

Installation:

pip install py-backwards

Compile code:

py-backwards -i src -o compiled -t 2.7

Testing compiled code

For testing compiled code with each supported python version you can use tox and tox-py-backwards. You need to install them:

pip install tox tox-py-backwards

Fill tox.ini (py_backwards = true in testenv section enables py-backwards), like:

[tox]
envlist = py27,py33,py34,py35,py36

[testenv]
deps = pytest
commands = py.test
py_backwards = true

And run tests with:

tox

Distributing compiled code

For distributing packages compiled with py-backwards you can use py-backwards-packager. Install it with:

pip install py-backwards-packager

And change setup import in setup.py to:

try:
    from py_backwards_packager import setup
except ImportError:
    from setuptools import setup

By default all targets enabled, but you can limit them with:

setup(...,
      py_backwards_targets=['2.7', '3.3'])

After that your code will be automatically compiled on bdist and bdist_wheel.

Running on systems without Python 3.3+

You can use docker for running py-backwards on systems without Python 3.3+, for example for testing on travis-ci with Python 2.7:

docker run -v $(pwd):/data/ nvbn/py-backwards -i example -o out -t 2.7

Development

Setup:

pip install .
python setup.py develop
pip install -r requirements.txt

Run tests:

 py.test -vvvv --capture=sys --enable-functional

Run tests on systems without docker:

 py.test -vvvv

Writing code transformers

First of all, you need to inherit from BaseTransformer, BaseNodeTransformer (if you want to use NodeTransfromer interface), or BaseImportRewrite (if you want just to change import).

If you use BaseTransformer, override class method def transform(cls, tree: ast.AST) -> TransformationResult, like:

from ..types import TransformationResult
from .base import BaseTransformer


class MyTransformer(BaseTransformer):
    @classmethod
    def transform(cls, tree: ast.AST) -> TransformationResult:
        return TransformationResult(tree=tree,
                                    tree_changed=True,
                                    dependencies=[])

If you use BaseNodeTransformer, override visit_* methods, for simplification this class have a whole tree in self._tree, you should also set self._tree_changed = True if the tree was changed:

from .base import BaseNodeTransformer


class MyTransformer(BaseNodeTransformer):
    dependencies = []  # additional dependencies

    def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.FunctionDef:
        self._tree_changed = True  # Mark that transformer changed tree
        return self.generic_visit(node)

If you use BaseImportRewrite, just override rewrites, like:

from .base import BaseImportRewrite


class MyTransformer(BaseImportRewrite):
    dependencies = ['pathlib2']

    rewrites = [('pathlib', 'pathlib2')]

After that you need to add your transformer to transformers.__init__.transformers.

It's hard to write code in AST, because of that we have snippets:

from ..utils.snippet import snippet, let, extend


@snippet
def my_snippet(class_name, class_body):
    class class_name:  # will be replaced with `class_name`
        extend(class_body)  # body of the class will be extended with `class_body`
        
        def fn(self):
            let(x)  # x will be replaced everywhere with unique name, like `_py_backwards_x_1`
            x = 10
            return x

And you can easily get content of snippet with:

my_snippet.get_body(class_name='MyClass',
                    class_body=[ast.Expr(...), ...])

Also please look at tree utils, it contains such useful functions like find, get_parent and etc.

Related projects

License MIT

More Repositories

1

thefuck

Magnificent app which corrects your previous console command.
Python
80,068
star
2

everpad

Evernote client well integrated with linux desktop
Python
1,165
star
3

django-bower

Easy way to use bower with your django project
Python
517
star
4

import_from_github_com

Python module finder/loader from github, like in golang
Python
152
star
5

python-yamusic

Python yandex music Library
Python
141
star
6

rhythmbox-gmusic

Rhythmbox Google Play Music Plugin
Python
86
star
7

djang0byte

Lightweight social networks engine written in python+django, that aims to bear great workloads.
Python
42
star
8

textarea-to-code-editor

Chrome extension for converting textarea to code editor
Clojure
41
star
9

series_list

Easy to use tool for automatically downloading episodes with subtitles
Python
39
star
10

subman

Search engine for subtitles
33
star
11

pytest-docker-pexpect

pytest plugin for writing functional tests with pexpect and docker
Python
31
star
12

telegram-swear-bot

Swearing telegram bot. It'll help you to speak like a gopnik.
JavaScript
28
star
13

vkvideo

Vkontakte video lens
Python
24
star
14

clj-di

Dependency injection and utility belt for testing for Clojure and ClojureScript
Clojure
24
star
15

coviolations_web

Tool for collecting and visualasing code violations.
Python
23
star
16

we-are-waiting

It's a bit boring to wait for a long script to complete, so it'll be better if someone will wait with you!
Go
19
star
17

roboarm

Python library for controlling owi robotic arm edge
Python
16
star
18

cantailme-server

Server side of cantail.me
Python
16
star
19

uglyrater

Engine based on tornado, mongodb and rabbitmq.
JavaScript
11
star
20

__transformers__

Experimental module for AST transformations
Python
10
star
21

nvbn.github.io

JavaScript
10
star
22

anndi

Experimental dependency injection that uses annotations.
Python
9
star
23

bocko-android

Bocko for Android
Clojure
9
star
24

l9t

Reusable typesafe kubernetes configurations with fast development cycle and no yaml
TypeScript
9
star
25

pyboard-play

A few games for pyboard/micropython
Python
9
star
26

DUCKHUNT

DUCKHUNT with phone as a gun written in reasonml
OCaml
7
star
27

sendToREPL

VS Code extension that sends selected code or line to REPL in terminal
JavaScript
7
star
28

mrw.wtf

Service for searching reaction gifs
Clojure
7
star
29

cantailme-client

Console client-sender for cantail.me
Python
7
star
30

shell_logger

Dead simple logger of shell commands with output and exit codes
Go
7
star
31

evilshortgen

Shortest and hackish tornado assync calls
Python
6
star
32

coviolations_app

coviolations.io
Python
6
star
33

pytoppa

Python package to ppa
Python
6
star
34

findZbomb

Write robot, find bomb and remove!
Python
6
star
35

cljs-test-example

Example project with cljs.test
Clojure
5
star
36

curfew-alarm

A small app that indicates when you need to go or stay at home.
TypeScript
5
star
37

mcoll

Modern API for python collections
Python
5
star
38

AnnoyMe

The most annoying schedule
TypeScript
4
star
39

PhoneTouch

Shows touch panel on your phone with actions depending on active app on your laptop/desktop.
JavaScript
4
star
40

microasync

Green threads and CSP for micropython
Python
4
star
41

debman

apt frontend with pacman syntax
Python
3
star
42

web-chess-game

Clojure
2
star
43

pyfunc

Examples for habr post
Python
2
star
44

tox-py-backwards

tox plugin for py-backwards
2
star
45

ng-gen

Angular module for replacing callbacks with es6 generators
JavaScript
2
star
46

ArSnake

AR SNAKE
JavaScript
2
star
47

python-sdk

Facebook Platform Python SDK
Python
1
star
48

robocam

OWI Robotic Arm + Raspberry pi + Arduino dashboard
Python
1
star
49

djang0parser

Html sanitaizer from djang0bye
Python
1
star
50

todomata

Todo application with calculation of short path for completing all tasks
Clojure
1
star
51

rdevices-client

rdevic.es client
Python
1
star
52

upsearch

App for finding file in nearest parent folder, very useful in aliases
Python
1
star
53

hn-past-decade

HN Trends of the Past Decade
Jupyter Notebook
1
star
54

subman-chrome

Chrome extension for subman.io written in clojurescript.
1
star
55

rdevices-server

rdevic.es server
Python
1
star
56

example-for-importing-different-packages-depending-on-python-version

Example for https://nvbn.github.io/2017/05/31/import-different/
Python
1
star