Niall,
I’m interested in making some significant adjustments to py2js. I had actually started writing the same thing last weekend using Python 2.6’s “ast” module (as opposed to 2.5’s “_ast” module). It is a relief and an encouragement to see all the code you’ve written & open-sourced.
My goal is a system that’s less ambitious than the pyjamas translator – just converting syntax and providing some of Python’s syntactic sugar without trying to smooth over all the differences between the two languages.
The #1 most important thing for me is generating clean code. This is critical both for debuggability and also for publishing the generated Javascript publicly (I would like to create cross-language modules that can be consumed both by Python users and Javascript users).
When I started using py2js, I found a number of things I would like to work on. One of them I would like to consider is porting it to the Python 2.6 “ast” module, as it is higher-level than the “_ast” module.
Another is allowing import of pure javascript namespaces that aren’t written in Python and aren’t being converted with py4js. For instance, webtoolkit’s base64 javascript module could be imported with python like “from webtoolkit import base64″, which might be translated to javascript like “import(‘base64′).from(‘webtoolkit’)”.
There are a number of smaller changes, things like putting variable declaration and assignment on the same line (“var n = …”), avoiding repeated, unnecessary variable declarations (if a variable is assigned in an “if” scope, a second, unnecessary “var” statement is generated), changing “new Kwarg” to something more compact, perhaps simply “kw”, removing some unnecessary grouping parenthesis and String() typing around string concatenation, and possibly looking at a non-generator approach to “for” loops.
I would also want to do lots of documentation/examples/doctests (using Sphinx) to really widen the web presence. I think a project like this could get a lot of steam if people saw the quality of the javascript being generated and that it really is a viable alternative to writing client-side code in JS (I would want it to operate cleanly with Prototype, jQuery and other JS frameworks).
I would also like to write a little helper utility that uses a FileSystemWatcher to auto-convert any .py file in a set of folders to a sibling .js file any time it is saved, to make the development save/refresh cycle faster.
Please let me know if you would like me to (a) move the py2js source from google code to my bitbucket account and start developing there (my preference), (b) contribute to the code in the google code repository, (c) fork the code and call my branch something else, with you pulling just the pieces (if any) that you want in the official branch, or (d) something else.
-- Peter Rust
Developer, Cornerstone Systems
Jonathan,
It’s encouraging to see your continued interest and that you’re chewing on these problems. I was thinking about this one myself early this morning – the difference between python dicts and javascript objects and the distinction python makes between dot notation and bracket notation, as opposed to Javascript treating them the same.
For a little background, the Prototype Javascript library (a Ruby-influenced lib) created a Hash object which, at first, decorated a standard JS object with helpful methods (.keys(), .values(), .each(), .clone(), .toQueryString(), etc). You would instantiate it via “new Hash({ 'my_key': 1 })” or via “$H({ 'my_key': 1 })” and it simply added functionality to the native Javascript object – you could retrieve properties via dot notation or bracket notation of the Hash function .get(). However, in later versions of the library (and the current version), they made a breaking change where the Hash object stored all the data hidden away in an internal object and the only way to set or get it was via .set() and .get(). They had to do this because of naming collisions between the data stored in the Hash and the functions decorating the hash.
As a Javascript developer, there are rare cases when I don’t know the keys that will be used and there’s a chance of collision with Hash methods – in this case I’ll use Prototype’s Hash object. But the vast majority of the time, I’m dealing with a distinct set of keys that I know in advance and I use native Javascript objects for convenience (my preference is for dot notation, with square backet notation being a distant second and get/set methods last).
Given that python is more strict in this case (allowing only square-bracket notation for dicts) and Javascript is more lenient (also allowing dot-notation) – and since we’re going in the PY -> JS direction, it would work to just retain the square-bracket notation in the emitted Javascript, with the exception of naming collisions, as you’ve mentioned. Not only is there a potential for
I’m thinking that the best thing to do would be to give users a choice, in their project-level configuration file, whether to convert bracket notation to get/set or getattr/setattr, or whether to leave them as bracket notation:
1. Keep as Bracket Notation:
· Cleaner Syntax (possibly we could allow dot notation as a variation)
· Faster
· With some custom JS code, can support custom getters/setters on modern browsers
· Potential for collisions if you don’t know the keys in advance
2. Convert to getattr()/setattr():
· Safer (no potential for collisions)
· Easy, native support for custom getters/setters
· Slightly slower
· Slightly addition syntactic cruft (possibly we could allow get()/set() as a variation)
I’m not sure which would be the default. #1 generates cleaner code and perhaps would be recommended for short & simple scripts, but #2 is more robust and would be recommended for longer scripts or more complex frameworks, especially when porting Python code which distinguishes between attributes and properties (like ElementTree). Perhaps there would even be two basic configuration sets – a more “robust mode” that is safe and is very extensible and a potentially-unsafe “super-clean” mode, where the developer would need to be more aware of the differences between the languages and know the pitfalls.
I’d love to hear additional thoughts and feedback, it’s never good to design in a vacuum…
-- peter
From: js...@googlegroups.com [mailto:js...@googlegroups.com] On
Behalf Of Jonathan Fine
Sent: Thursday, April 22, 2010 11:32 PM
To: js...@googlegroups.com
Subject: Re: proposed py2js changes
Here's something that's come to my mind recently. It's about how a.b and a['b'] are identical in JavaScript and different in Python (of course). Also, in JavaScript hashes don't start empty. They inherit attributes such as toString.
Jonathan,
Forgot to respond to this implementation idea:
> they prefix the key string with a character, say '#'
Not a bad idea – either prefix with a character that’s not valid for dot-notation or hide them away in an inner object.
-- peter
From: js...@googlegroups.com [mailto:js...@googlegroups.com] On Behalf Of Jonathan Fine
Sent: Thursday, April 22, 2010 11:32 PM
To: js...@googlegroups.com
Subject: Re: proposed py2js changes
Here's something that's come to my mind recently. It's about how a.b and a['b'] are identical in JavaScript and different in Python (of course). Also, in JavaScript hashes don't start empty. They inherit attributes such as toString.
Jonathan,
Yes, I am anxious to try these ideas in real-world software and see how (or if) they fly – I’m sure things will evolve quite a bit once the rubber meets the road. We’ll start at “0.1” (or is it 0.2… or 0.3?) and let things vary wildly as we’re figuring out what works and what doesn’t.
I found a couple existing “pythonic” Javascript libraries:
· http://code.google.com/p/protopy/
Google’s Closure Library also has some pythonic things, including modules and iteration (a next() method and a StopIteration exception). I haven’t looked too deeply yet, but we should look carefully at what these offer to avoid spending our time re-doing things that have already been done. Plus, we may be able to learn from others’ experiences. We may also find some people who are interested in helping with this project.
We don’t need to create a full client-side library (dealing with ajax/dom/etc) – users can easily import any of the existing libs for that, but we do need to cover the basics: Python’s built-in list and dict functionality as well as some of the string functionality and the basic Python built-in functions. Niall has already done some of this and for starters we only need to focus on implementing the 20% that is used 80% of the time and clearly documenting what is (and isn’t) implemented. Some things that are already built into Javascript require only slight modification (like wrapping Javascript’s built-in array/string slice() function with a custom function that can convert negative indexes based on the length of the array/string).
Speaking of Miller Columns, I have a set of helper functions that help with grouping data and generating tables – they’re currently written in Javascript, but I intend to port them to Python and use py2js to make them available in both Python and Javascript. They will also give it a good test-run. Also, I have a public-facing app, Project Recon, that I’d love to convert someday as well (I’m almost done converting it from IE+.Net to Firefox/XULRunner… but it’s still mostly Javascript).
-- peter
From: js...@googlegroups.com [mailto:js...@googlegroups.com] On
Behalf Of Jonathan Fine
Sent: Friday, April 23, 2010 11:33 AM
To: js...@googlegroups.com
Subject: Re: proposed py2js changes
Peter
Niall,
Yes, this will make a big difference. The globals in the pythonic lib could
> Using one of those pythonic JS libraries
> or creating such a layer using the same ideas
> should simplify the generated code
be imported into each emitted file using a module importer, like Jonathan's
jsmod, so that the lib isn't cluttering Javascript's global namespace.
However, if the lib changes Array.prototype or Object.prototype, it'll be
difficult to mask those changes from other (non py2js-generated) modules.
Any chance you could send me the changes somehow? I'll be working with it
> I made one or two minor changes to make it work with python 2.6,
> otherwise its exactly identical to the code you've been looking at
under Python 2.6 this weekend -- I'd like to avoid redoing things you've
already done...
Jonathan,
Yes, (1) modifying Object.prototype is almost always a bad idea, as everything inherits from Object. This pretty much forces us down the path of a dict wrapper, as opposed to being able to use “{ … }” notation for dicts. However, I think I would modify Array.prototype (as prototypejs.org does), so that we can use “[ … ]” notation. We could have a flag to avoid this (some JS developers don’t like to modify *any* built-in prototypes) – if this flag is set, arrays would be created with “list( … )” instead of “[ … ]”.
As for (2), I do strongly agree with you that there should just be one global – say JSMOD, or some other object or function that does importing. Then all other modules should be hidden inside of that, as you do with your internal “module” object that’s hidden via a closure. However, when designing individual modules, I don’t usually use closures to hide properties. I like python’s approach of using single and double-underscores for things that are private or really private and leaving it at that. But I’m ok with other developers using closures to hide private things and I could do so as well if that’s the consensus for the libraries we’re working on together. The only thing I would push for is that we make it possible, within a module, to access sub-modules and functions via dot-notation. That way you have the option of doing a single import of the top-level package and then can use dot-notation to drill down as far as you need to.
Let me know what you think,
-- peter
From: js...@googlegroups.com [mailto:js...@googlegroups.com] On
Behalf Of Jonathan Fine
Sent: Saturday, April 24, 2010 10:28 AM
To: js...@googlegroups.com
Subject: Re: proposed py2js changes
Hi
The only thing I would push for is that we make it possible, within a module, to access sub-modules and functions via dot-notation. That way you have the option of doing a single import of the top-level package and then can use dot-notation to drill down as far as you need to.