Wednesday, February 3, 2016

Sane ECMAScript 6 Generators

This post is cross posted in Medium, here.

I recently found one interesting thing about ES6 Generators. I come from Python background and I understood generators as in Python. So, I expected the following Python code's equivalent ECMAScript 6 code also to work as well.
>>> numbers = (num for num in range(10))
>>> for num in numbers:
...   if num == 3:
...     break
... 
>>> next(numbers)
4
You can find the online demo for this Python program, here.

But then, when I used Babel to transpile the following code and executed it
function* NumberGenerator() {
  for (var i = 0; i < 10; i += 1) {
    yield i;
  }
}

var numbers = NumberGenerator();

for (var num of numbers) {
  if (num === 3) {
    break;
  }
}

console.log(numbers.next());
// {"done":true}
You can find the online demo for this JavaScript program, made with Babel's REPL, here. As you see here, when I broke out of the loop, the Generator Object got closed. This was pointed out to me by Logan Smyth in Babel's Slack discussion. I was really surprised by this behavior and found the 13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation ( lhs, stmt, iterator, lhsKind, labelSet ) section in the ECMAScript 6 Specification, which says
If LoopContinues(result, labelSet) is false, return IteratorClose(iterator, UpdateEmpty(result, V)).
I am not sure about the rationale behind that decision, but I am convinced that it would effectively limit the potential of the Generators. So I decided to fix this.

Sane Generators

To close the iterator, Iterator.prototype.return is called. (At the time of this writing, not many JavaScript Engines support this function. You can find the support for this feature by popular engines, here.) So I decided to override that and allow the actual return function to be invoked only when explicitly called with an argument.
function returnFunction(originalReturn, genObject) {
  return function(arg) {
    return arguments.length ? originalReturn.call(genObject, arg) : {
      done: false
    };
  };
}

function SaneGenerator(genObject) {
  var originalReturn = genObject['return'];

  if (typeof originalReturn === 'function') {
    Object.defineProperty(genObject, 'return', {
      value: returnFunction(originalReturn, genObject)
    });
  }

  return genObject;
}
You can see the actual and complete implementation in my GitHub repository, https://github.com/thefourtheye/sane-generator. Now, you can use the SaneGenerator like this
function* NumberGenerator() {
  for (var i = 0; i < 10; i += 1) {
    yield i;
  }
}

var numbers = SaneGenerator(NumberGenerator());

for (var num of numbers) {
  if (num === 3) {
    break;
  }
}

console.log(numbers.next());
// {"value":4,"done":false}
You can find the online demo for this JavaScript program, made with Babel's REPL, here.

NPM Module

This is available as an NPM module now. https://www.npmjs.com/package/sane-generator

Sunday, January 10, 2016

Sublime Text 3 unhiding menu in Ubuntu - simpler solution

The method which I mentioned in this blog post of mine, solves the problem, but then you have to
  1. Close the Sublime Text
  2. Fire up a terminal
  3. Execute a command or two
  4. Open Sublime Text again
Quite a lot of steps to do. But then I found a simpler solution today and it works like a charm for me. The solution is to use "Keyboard Shortcuts" :-) Let me explain in detail.
  1. Click Preferences -> Key Bindings - User
  2. In the window that opens, you need to add a new keyboard shortcut for the command "toggle_menu". For example, this is how my shortcuts looks like
    [{"keys": ["ctrl+\\"], "command": "toggle_side_bar"},
     {"keys": ["ctrl+shift+m"], "command": "toggle_menu"}]
  3. Save the file and start doing your happy dance.
Now, you can simply press the keyboard shortcut whatever you choose to assign to toggle menu :-)

Sunday, February 15, 2015

Python 3.5 and Django 1.7's HTMLParseError

I just updated my Python installation to 3.5 Alpha 1 and started with Django today and I hit a road block already. It all went fine, when I created the virtual environment and installed Django 1.7.4 in it. And then when I did startproject, I got AttributeError: module 'html.parser' has no attribute 'HTMLParseError'.
(py3.5venv) ➜  myProject git:(master) ✗ django-admin.py startproject myProject
Traceback (most recent call last):
  File "/py3.5venv/bin/django-admin.py", line 5, in 
    management.execute_from_command_line()
  File "/py3.5venv/lib/python3.5/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/py3.5venv/lib/python3.5/site-packages/django/core/management/__init__.py", line 354, in execute
    django.setup()
  File "/py3.5venv/lib/python3.5/site-packages/django/__init__.py", line 18, in setup
    from django.utils.log import configure_logging
  File "/py3.5venv/lib/python3.5/site-packages/django/utils/log.py", line 13, in 
    from django.views.debug import ExceptionReporter, get_exception_reporter_filter
  File "/py3.5venv/lib/python3.5/site-packages/django/views/debug.py", line 10, in 
    from django.http import (HttpResponse, HttpResponseServerError,
  File "/py3.5venv/lib/python3.5/site-packages/django/http/__init__.py", line 4, in 
    from django.http.response import (
  File "/py3.5venv/lib/python3.5/site-packages/django/http/response.py", line 13, in 
    from django.core.serializers.json import DjangoJSONEncoder
  File "/py3.5venv/lib/python3.5/site-packages/django/core/serializers/__init__.py", line 23, in 
    from django.core.serializers.base import SerializerDoesNotExist
  File "/py3.5venv/lib/python3.5/site-packages/django/core/serializers/base.py", line 6, in 
    from django.db import models
  File "/py3.5venv/lib/python3.5/site-packages/django/db/models/__init__.py", line 6, in 
    from django.db.models.query import Q, QuerySet, Prefetch  # NOQA
  File "/py3.5venv/lib/python3.5/site-packages/django/db/models/query.py", line 13, in 
    from django.db.models.fields import AutoField, Empty
  File "/py3.5venv/lib/python3.5/site-packages/django/db/models/fields/__init__.py", line 18, in 
    from django import forms
  File "/py3.5venv/lib/python3.5/site-packages/django/forms/__init__.py", line 6, in 
    from django.forms.fields import *  # NOQA
  File "/py3.5venv/lib/python3.5/site-packages/django/forms/fields.py", line 18, in 
    from django.forms.utils import from_current_timezone, to_current_timezone
  File "/py3.5venv/lib/python3.5/site-packages/django/forms/utils.py", line 15, in 
    from django.utils.html import format_html, format_html_join, escape
  File "/py3.5venv/lib/python3.5/site-packages/django/utils/html.py", line 16, in 
    from .html_parser import HTMLParser, HTMLParseError
  File "/py3.5venv/lib/python3.5/site-packages/django/utils/html_parser.py", line 12, in 
    HTMLParseError = _html_parser.HTMLParseError
AttributeError: module 'html.parser' has no attribute 'HTMLParseError'
Then Antti Haapala (a friend from Stakoverflow's Python Chat room) found out that it is because HTMLParseError is deprecated from Python 3.3 onwards and removed in Python 3.5, as per the official documentation. He continued the investigation and found out that django guys are already aware of this, as they have a bug raised, and the fix has been already delivered to the code repository.
Now, all we can do is, wait for the version of django which officially supports Python 3.5 or manually patch html_parser.py file as per the above shown fix. Even if we patch it manually, there is no guarantee that everything else will work fine.

Sunday, February 1, 2015

Ubuntu overlapping displays while scaling dual monitor setup

Today, again I wanted to change my screen's display size, in my Ubuntu 14.04. This time I have a dual monitor setup and I tried all the commands I mentioned in my blog post Ubuntu scaling the display (Zooming).
➜  ~  xrandr
Screen 0: minimum 8 x 8, current 2560 x 1024, maximum 16384 x 16384
VGA-0 connected 1280x1024+1280+0 (normal left inverted right x axis y axis) 376mm x 301mm
    1280x1024      60.0*+   75.0  
    1152x864       75.0  
    1024x768       75.0     60.0  
    800x600        75.0     60.3  
    640x480        75.0     59.9  
LVDS-0 connected (normal left inverted right x axis y axis)
    1600x900       60.0 +   40.0  
DP-0 connected primary 1280x1024+0+0 (normal left inverted right x axis y axis) 376mm x 301mm
    1280x1024      60.0*+   75.0  
    1152x864       75.0  
    1024x768       75.0     60.0  
    800x600        75.0     60.3  
    640x480        75.0     59.9  
DP-1 disconnected (normal left inverted right x axis y axis)
HDMI-0 disconnected (normal left inverted right x axis y axis)
DP-2 disconnected (normal left inverted right x axis y axis)
DP-3 disconnected (normal left inverted right x axis y axis)
➜  ~  xrandr --output DP-0 --scale 1.25x1.25
➜  ~  xrandr --output VGA-0 --scale 1.25x1.25
➜  ~  xrandr
Screen 0: minimum 8 x 8, current 2880 x 1280, maximum 16384 x 16384
VGA-0 connected 1600x1280+1280+0 (normal left inverted right x axis y axis) 376mm x 301mm
    1280x1024      60.0*+   75.0  
    1152x864       75.0  
    1024x768       75.0     60.0  
    800x600        75.0     60.3  
    640x480        75.0     59.9  
LVDS-0 connected (normal left inverted right x axis y axis)
    1600x900       60.0 +   40.0  
DP-0 connected primary 1600x1280+0+0 (normal left inverted right x axis y axis) 376mm x 301mm
    1280x1024      60.0*+   75.0  
    1152x864       75.0  
    1024x768       75.0     60.0  
    800x600        75.0     60.3  
    640x480        75.0     59.9  
DP-1 disconnected (normal left inverted right x axis y axis)
HDMI-0 disconnected (normal left inverted right x axis y axis)
DP-2 disconnected (normal left inverted right x axis y axis)
DP-3 disconnected (normal left inverted right x axis y axis)
Though it changes the display size, both the screens had overlapping images. After so much analysis, I found one inconsistency in the output of xrandr, before and after scaling. Note the first two lines.
Screen 0: minimum 8 x 8, current 2560 x 1024, maximum 16384 x 16384
VGA-0 connected 1280x1024+1280+0 (normal left inverted right x axis y axis) 376mm x 301mm
...
Screen 0: minimum 8 x 8, current 2880 x 1280, maximum 16384 x 16384
VGA-0 connected 1600x1280+1280+0 (normal left inverted right x axis y axis) 376mm x 301mm
Individual screen's resolution has scaled to 1600x1280 from 1280x1024, but the total screen size is 2880 x 1280, also the screen's position is 1280x0 instead of 1600x0. So, to get over the overlapping problem, I used the following command, after reboot, and it worked
➜  ~  xrandr --output DP-0 --scale 1.25x1.25 --pos 0x0 --output VGA-0 --scale 1.25x1.25 --pos 1600x0
➜  ~  xrandr
Screen 0: minimum 8 x 8, current 3200 x 1280, maximum 16384 x 16384
VGA-0 connected 1600x1280+1600+0 (normal left inverted right x axis y axis) 376mm x 301mm
    1280x1024      60.0*+   75.0  
    1152x864       75.0  
    1024x768       75.0     60.0  
    800x600        75.0     60.3  
    640x480        75.0     59.9  
LVDS-0 connected (normal left inverted right x axis y axis)
    1600x900       60.0 +   40.0  
DP-0 connected primary 1600x1280+0+0 (normal left inverted right x axis y axis) 376mm x 301mm
    1280x1024      60.0*+   75.0  
    1152x864       75.0  
    1024x768       75.0     60.0  
    800x600        75.0     60.3  
    640x480        75.0     59.9  
DP-1 disconnected (normal left inverted right x axis y axis)
HDMI-0 disconnected (normal left inverted right x axis y axis)
DP-2 disconnected (normal left inverted right x axis y axis)
DP-3 disconnected (normal left inverted right x axis y axis)
Now, you see that both the actual size of the screen and the starting position of the second screen is set properly.
Screen 0: minimum 8 x 8, current 3200 x 1280, maximum 16384 x 16384
VGA-0 connected 1600x1280+1600+0 (normal left inverted right x axis y axis) 376mm x 301mm
...

Tuesday, December 30, 2014

Python's venv problem with ensurepip in Ubuntu

Ubuntu 14.04's Python 3.4 installation has a problem with ensurepip module, as described in this bug. So if you follow the steps mentioned in the official documentation, you would see an error message like this
➜  Python  python3 -m venv py3.4venv
Error: Command '['/home/thefourtheye//py34venv/bin/python3', '-Im', 'ensurepip', '--upgrade', '--default-pip']' returned non-zero exit status 1
To resolve this problem, first install the venv, without pip, like this
python3 -m venv py3.4venv --without-pip
And then if you install pip, like this, it will still fail
(py3.4venv) ➜  py3.4venv  python -m ensurepip --upgrade
/home/thefourtheye/Python/py3.4venv/bin/python: No module named ensurepip
(py3.4venv) ➜  py3.4venv  python
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
...
So, you need to install, pip separately, like mentioned in the pip's official documentation. So, the actual list of steps go like this
➜  Python  python3 -m venv py3.4venv --without-pip 
➜  Python  cd py3.4venv 
➜  py3.4venv  source bin/activate
(py3.4venv) ➜  py3.4venv  wget https://bootstrap.pypa.io/get-pip.py
--2014-12-30 14:35:34--  https://bootstrap.pypa.io/get-pip.py
Resolving bootstrap.pypa.io (bootstrap.pypa.io)... 103.245.222.175
Connecting to bootstrap.pypa.io (bootstrap.pypa.io)|103.245.222.175|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1581355 (1.5M) [text/x-python]
Saving to: ‘get-pip.py’

100%[=====================================================================================================================>] 15,81,355    129KB/s   in 8.9s   

2014-12-30 14:35:43 (173 KB/s) - ‘get-pip.py’ saved [1581355/1581355]

(py3.4venv) ➜  py3.4venv  python get-pip.py 
Collecting pip
  Downloading pip-6.0.3-py2.py3-none-any.whl (1.3MB)
    100% |################################| 1.3MB 139kB/s 
Collecting setuptools
  Downloading setuptools-9.1-py2.py3-none-any.whl (552kB)
    100% |################################| 552kB 180kB/s 
Installing collected packages: setuptools, pip


Successfully installed pip-6.0.3 setuptools-9.1
(py3.4venv) ➜  py3.4venv  deactivate 
➜  py3.4venv  source bin/activate                      
(py3.4venv) ➜  py3.4venv  pip install django
Collecting django
  Using cached Django-1.7.1-py2.py3-none-any.whl
Installing collected packages: django

Successfully installed django-1.7.1
(py3.4venv) ➜  py3.4venv  which pip
/home/thefourtheye/Python/py3.4venv/bin/pip
(py3.4venv) ➜  py3.4venv