Skip to content

Commit

Permalink
__getattr__, __setattr__, proxies testlet, div. new aliases, e.g. clear
Browse files Browse the repository at this point in the history
  • Loading branch information
JdeH committed Nov 14, 2016
1 parent e2e1f92 commit 1d97086
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 26 deletions.
6 changes: 4 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Development build status
:target: https://travis-ci.org/QQuick/Transcrypt

The icon above shows the outcome of the continous integration test that is done on Linux after each commit.
The test consists of running a set of testlets, systematically covering all facilities of Transcrypt. Each testlet performs an automated back to test against CPython.
The test consists of running a set of testlets, systematically covering all facilities of Transcrypt. Each testlet performs an automated back to back test against CPython.

The full set of testlets is described in the documentation and comes with the distribution.
Since the branching model has been deliberately kept simple, continuous integration may be transiently broken.
Expand All @@ -102,12 +102,14 @@ Each release, on the other hand, is preceded by at least the following tests:

- The automated back to back test described above, not only on Linux but also on Windows and, in case of relevant issues, on OsX.
- Automated compilation of the manual tests, human exercising of the resulting applications and a visual check of the results.
- Automated compilation of the demo's, manual exercising of the resulting applications and a visual check of the results.
- Automated compilation of the demo's, human exercising of the resulting applications and a visual check of the results.
- A documentation build, followed by a visual sample check.

What's new in the latest commits
--------------------------------

- Enhancement for issue: #89 and #149: __getattr__ and __setattr__ are now supported, requiring the -e 6 switch. Testlet 'proxies' added.
- New aliases added to prevent name clashes. The orignal name can always be reached by prepending js_. So e.g. if you need 'clear' in JS, use 'js_clear' in Python. A complete list of aliases is in the docs. Any alias can be undefined to maintain backward compatibility, e.g __pragma__ ('noalias', 'clear').
- Enhancement for issue #169: Add support for float('inf') and float('-inf')
- Python 3.6 numbers with dashes added
- Python 3.6 fstrings added
Expand Down
2 changes: 2 additions & 0 deletions transcrypt/demos/pong/pong.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
document = window = Math = Date = 0 # Prevent complaints by optional static checker
__pragma__ ('noskip')

__pragma__ ('noalias', 'clear')

from com.fabricjs import fabric

orthoWidth = 1000
Expand Down
12 changes: 12 additions & 0 deletions transcrypt/development/automated_tests/transcrypt/autotest.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
import nonlocals
import operator_overloading
import properties

__pragma__ ('ifdef', '__esv6__')
if '__esv6__' in __symbols__:
import proxies
__pragma__ ('endif')

import set_comprehensions
import simple_and_augmented_assignment
import truthyness
Expand Down Expand Up @@ -112,6 +118,12 @@
autoTester.run (nonlocals, 'nonlocals')
autoTester.run (operator_overloading, 'operator_overloading')
autoTester.run (properties, 'properties')

__pragma__ ('ifdef', '__esv6__')
if '__esv6__' in __symbols__:
autoTester.run (proxies, 'proxies')
__pragma__ ('endif')

autoTester.run (set_comprehensions, 'set_comprehensions')
autoTester.run (simple_and_augmented_assignment, 'simple_and_augmented_assignment')
autoTester.run (truthyness, 'truthyness')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ def sort (self):
for row in sortTest.rows:
autoTester.check ('{}<br>'.format (','.join ([word for word in row])))

autoTester.check ('<br><br>Issue 148<br>') # Allow key a.o. key 'items' in dict

aDict = {
'items': [4, 5, 6]
}

for aKey, aValue in aDict.items ():
autoTester.check ('{}: {}'.format (aKey, aValue))

autoTester.check ('<br><br>Issue 169<br>') # Add support for float('inf') and float('-inf')

autoTester.check (int (1 / float ('inf')), int (1 / float ('-inf')))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from org.transcrypt.stubs.browser import __pragma__

def run (autoTester):
class A:
def __init__ (self):
self.p = 1
self.q = 2

class B (A):
def __getattr__ (self, name):
return 'Faked {}'.format (name)

class C (A):
def __setattr__ (self, name, value):
autoTester.check ('Set faked {}'.format (name))

A.__setattr__ (self, name, value)
# Needed for CPython, inherited from class 'object'
# Transcrypt doesn't need it, if there's no __setattrib__ it will just use self [name] = value

class D (B, C):
pass

a = A ()
b = B ()
c = C ()
d = D ()

autoTester.check (a.p, a.q)
a.p = 3
autoTester.check (a.p, a.q)

autoTester.check (b.p, b.q, b.r, b.s)
b.p = 4
b.r = 5
autoTester.check (b.p, b.q, b.r, b.s)

autoTester.check (c.p, c.q)
c.p = 6
c.q = 7
autoTester.check (c.p, c.q)

autoTester.check (d.p, d.q, d.r, d.s)
d.p = 8
d.q = 9
d.r = 10
d.s = 11
autoTester.check (d.p, d.q, d.r, d.s)
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,16 @@ def g (self):

tolerant = Tolerant () # Unexpected *args and **kwargs behaviour

output ('T', 'a' in tolerant)
output ('T', 'f' in tolerant)
output ('T', 'a' in Tolerant)
output ('T', 'f' in Tolerant)
output ('F', 'b' in Tolerant)
output ('T', 'g' in Tolerant)
output ('F', 'h' in Tolerant)

output ('F', 'a' in tolerant)
output ('F', 'f' in tolerant)
output ('T', 'b' in tolerant)
output ('T', 'g' in tolerant)
output ('F', 'g' in tolerant)
output ('F', 'h' in tolerant)

output ('<br>Issue 102')
Expand All @@ -51,7 +57,7 @@ def g (self):
__pragma__ ('ifdef', '__esv6__')
output ('[object Object] rather than null in previous line')
__pragma__ ('else')
output ('null rather than [object Object] in previous line')
output ('None rather than [object Object] in previous line')
__pragma__ ('endif')

output ('<br>Issue 130') # Add pragma to optionally handle % the JS way
Expand Down
3 changes: 2 additions & 1 deletion transcrypt/docs/sphinx/differences_cpython.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Differences due to interoperability with JavaScript and JavaScript libraries
- Any amount of literal JavaScript can be included in-line or from a separate file using :ref:`\_\_pragma\_\_ ('js', ..., ...) <pragma_js>`.
- The print function can be used to print to a DOM element or to the browser console.
- The methods console.dir and console.log are also available when programming for the browser, as are in fact all JavaScript facilities, including the complete DOM-manipulation API.
- Certain identifiers have special meaning in JavaScript. In most cases they still can be used in Transcrypt, since they are aliased to other identifiers. Identifiers starting with 'py\_' are to be avoided, since they have special meaning in Transcrypt. It would have been possible to make clashes harder, by using e.g. @ and \_\_ in but this would have made the JavaScript code harder to read.
- Certain identifiers are reserved in JavaScript. In most cases they still can be used in Transcrypt, since they are aliased to other identifiers. Identifiers and directory keys starting with 'py\_' are to be avoided, since many of them have special meaning in Transcrypt. It would have been possible to make clashes even more rare, by using e.g. @ and \_\_ in but this would have made the JavaScript code harder to read. Note that you can define a local alias yourself if you still want to use a reserved identifier.
- The name *type* cannot be used as an ordinary identifier.
- You can use 'require' to load JavaScript modules on the fly and access them just as you would from JavaScript.

Differences due to running Transcryp applications in the browser, rather than on the desktop
Expand Down
2 changes: 1 addition & 1 deletion transcrypt/modules/org/transcrypt/__base__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class __Envir__:
def __init__ (self):
self.transpiler_name = 'transcrypt'
self.transpiler_version = '3.6.0'
self.transpiler_version = '3.6.1'
self.target_subdir = '__javascript__'

__envir__ = __Envir__ ()
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,6 @@ __pragma__ ('endif')
}
__all__.__merge__ = __merge__;

/* Not needed anymore?
// Make console.log understand apply
console.log.apply = function () {
print ([] .slice.apply (arguments) .slice (1));
};
*/

// Manipulating attributes by name

var dir = function (obj) {
Expand Down Expand Up @@ -915,7 +908,7 @@ __pragma__ ('endif')
return this.union (other) .difference (this.intersection (other));
};

Array.prototype.update = function () { // O (n)
Array.prototype.py_update = function () { // O (n)
var updated = [] .concat.apply (this.slice (), arguments) .sort ();
this.clear ();
for (var i = 0; i < updated.length; i++) {
Expand Down Expand Up @@ -1265,13 +1258,13 @@ __pragma__ ('else')
Object.defineProperty (instance, 'py_keys', {value: __keys__, enumerable: false});
Object.defineProperty (instance, '__iter__', {value: function () {new __PyIterator__ (this.py_keys ());}, enumerable: false});
Object.defineProperty (instance, Symbol.iterator, {value: function () {new __JsIterator__ (this.py_keys ());}, enumerable: false});
Object.defineProperty (instance, 'items', {value: __items__, enumerable: false});
Object.defineProperty (instance, 'del', {value: __del__, enumerable: false});
Object.defineProperty (instance, 'clear', {value: __clear__, enumerable: false});
Object.defineProperty (instance, 'get', {value: __getdefault__, enumerable: false});
Object.defineProperty (instance, 'setdefault', {value: __setdefault__, enumerable: false});
Object.defineProperty (instance, 'py_items', {value: __items__, enumerable: false});
Object.defineProperty (instance, 'py_del', {value: __del__, enumerable: false});
Object.defineProperty (instance, 'py_clear', {value: __clear__, enumerable: false});
Object.defineProperty (instance, 'py_get', {value: __getdefault__, enumerable: false});
Object.defineProperty (instance, 'py_setdefault', {value: __setdefault__, enumerable: false});
Object.defineProperty (instance, 'py_pop', {value: __pop__, enumerable: false});
Object.defineProperty (instance, 'update', {value: __update__, enumerable: false});
Object.defineProperty (instance, 'py_update', {value: __update__, enumerable: false});
return instance;
}
__pragma__ ('endif')
Expand Down
40 changes: 36 additions & 4 deletions transcrypt/modules/org/transcrypt/__javascript__/__core__.mod.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@
};
__all__.__init__ = __init__;


__pragma__ ('ifdef', '__esv6__')
// Proxy switch, controlled by __pragma__ ('proxy') and __pragma ('noproxy')
var __proxy__ = false; // No use assigning it to __all__, only its transient state is important
__pragma__ ('endif')


// Since we want to assign functions, a = b.f should make b.f produce a bound function
// So __get__ should be called by a property rather then a function
// Factory __get__ creates one of three curried functions for func
Expand Down Expand Up @@ -74,7 +81,7 @@
}
}
__all__.__get__ = __get__;

// Mother of all metaclasses
var py_metatype = {
__name__: 'type',
Expand Down Expand Up @@ -133,7 +140,7 @@ __pragma__ ('endif')
py_metatype.__metaclass__ = py_metatype;
__all__.py_metatype = py_metatype;

// Mother of all classes
// Mother of all classes
var object = {
__init__: function (self) {},

Expand All @@ -148,10 +155,35 @@ __pragma__ ('endif')
// The descriptor produced by __get__ will return the right method flavor
var instance = Object.create (this, {__class__: {value: this, enumerable: true}});

__pragma__ ('ifdef', '__esv6__')
if ('__getattr__' in this || '__setattr__' in this) {
instance = new Proxy (instance, {
get: function (target, name) {
var result = target [name];
if (result == undefined) { // Target doesn't have attribute named name
return target.__getattr__ (name);
}
else {
return result;
}
},
set: function (target, name, value) {
try {
target.__setattr__ (name, value);
}
catch (exception) { // Target doesn't have a __setattr__ method
target [name] = value;
}
return true;
}
})
}
__pragma__ ('endif')

// Call constructor
this.__init__.apply (null, [instance] .concat (args));
// Return instance

// Return constructed instance
return instance;
}
};
Expand Down
6 changes: 6 additions & 0 deletions transcrypt/modules/org/transcrypt/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,12 +470,16 @@ def __init__ (self, module):
('js_and', 'and'),
('arguments', 'py_arguments'), ('js_arguments', 'arguments'),
('case', 'py_case'),
('clear', 'py_clear'), ('js_clear', 'clear'),
('default', 'py_default'),
('del', 'py_del'), ('js_del', 'del'),
('false', 'py_false'),
('js_from', 'from'),
('Infinity', 'py_Infinity'), ('js_Infinity', 'Infinity'),
('isNaN', 'py_isNaN'), ('js_isNaN', 'isNaN'),
('iter', 'py_iter'), ('js_iter', 'iter'),
('items', 'py_items'), ('js_items', 'items'),
('get', 'py_get'), ('js_get', 'get'),
('keys', 'py_keys'), ('js_keys', 'keys'),
('name', 'py_name'), ('js_name', 'name'),
('NaN', 'py_NaN'), ('js_NaN', 'NaN'),
Expand All @@ -489,7 +493,9 @@ def __init__ (self, module):
('split', 'py_split'), ('js_split', 'split'),
('switch', 'py_switch'),
('type', 'py_metatype'), ('js_type', 'type'), # Only for the type metaclass, the type operator is dealth with separately in visit_Call
('update', 'py_update'), ('js_update', 'update'),
('reversed', 'py_reversed'), ('js_reversed', 'reversed'),
('setdefault', 'py_setdefault'), ('js_setdefault', 'setdefault'),
('true', 'py_true'),
('undefined', 'py_undefined'), ('js_undefined', 'undefined'),

Expand Down

0 comments on commit 1d97086

Please sign in to comment.