1. masklinn
  2. py.js

Commits

masklinn  committed 12c1b7f

Implement the abs builtin, and corresponding hook in float. Fixes #14

  • Participants
  • Parent commits 2b62048
  • Branches default

Comments (0)

Files changed (3)

File lib/py.js

View file
  • Ignore whitespace
         throw new Error("Could not convert " + val + " to a pyval");
     }
 
+    var typename = function (obj) {
+        if (obj.__class__) { // py type
+            return obj.__class__.__name__;
+        } else if(typeof obj !== 'object') { // JS primitive
+            return typeof obj;
+        } else { // JS object
+            return obj.constructor.name;
+        }
+    };
     // JSAPI, JS-level utility functions for implementing new py.js
     // types
     py.py = {};
         if (py.PY_isInstance(v, py.str)) {
             return v;
         }
-        var typename;
-        if (v.__class__) { // py type
-            typename = v.__class__.__name__;
-        } else if(typeof v !== 'object') { // JS primitive
-            typename = typeof v;
-        } else { // JS object
-            typename = v.constructor.name;
-        }
         throw new Error(
-            'TypeError: __str__ returned non-string (type '+typename+')');
+            'TypeError: __str__ returned non-string (type '
+                + typename(v)
+                +')');
     };
     py.PY_isInstance = function (inst, cls) {
         var fn = function () {};
         }
         throw new Error(
             "TypeError: __nonzero__ should return bool, returned "
-                + res.__class__.__name__);
+                + typename(res));
     };
     py.PY_not = function (o) {
         return !py.PY_isTrue(o);
         if (!o.__len__) {
             throw new Error(
                 "TypeError: object of type '" +
-                    o.__class__.__name__ +
+                    typename(o) +
                     "' has no len()");
         }
         var v = o.__len__();
         if (!o.__neg__) {
             throw new Error(
                 "TypeError: bad operand for unary -: '"
-                    + o.__class__.__name
+                    + typename(o)
                     + "'");
         }
         return o.__neg__();
         if (!o.__pos__) {
             throw new Error(
                 "TypeError: bad operand for unary +: '"
-                    + o.__class__.__name
+                    + typename(o)
                     + "'");
         }
         return o.__pos__();
                     return;
                 }
                 throw new Error('TypeError: __float__ returned non-float (type ' +
-                                res.__class__.__name__ + ')');
+                                typename(res) + ')');
             }
             throw new Error('TypeError: float() argument must be a string or a number');
         },
             }
             return this._value >= other._value ? py.True : py.False;
         },
+        __abs__: function () {
+            return py.float.fromJSON(
+                Math.abs(this._value));
+        },
         __add__: function (other) {
             if (!py.PY_isInstance(other, py.float)) {
                 return py.NotImplemented;
         }
     });
 
+    py.abs = new py.PY_def.fromJSON(function abs() {
+        var args = py.PY_parseArgs(arguments, ['number']);
+        if (!args.number.__abs__) {
+            throw new Error(
+                "TypeError: bad operand type for abs(): '"
+                    + typename(args.number)
+                    + "'");
+        }
+        return  args.number.__abs__();
+    });
     py.len = new py.PY_def.fromJSON(function len() {
         var args = py.PY_parseArgs(arguments, ['object']);
         return py.float.fromJSON(py.PY_size(args.object));
         list: py.list,
         dict: py.dict,
 
+        abs: py.abs,
         len: py.len,
         isinstance: py.isinstance,
         issubclass: py.issubclass,

File test/builtins/abs.js

View file
  • Ignore whitespace
+var py = require('../../lib/py.js'),
+    expect = require('expect.js');
+
+describe('py.abs', function () {
+    it('should exist', function () {
+        expect(!!py.abs).to.be(true);
+    });
+    it('should be a builtin', function () {
+        expect(py.eval('bool(abs)')).to.be(true);
+    });
+    it('should call its hook method', function () {
+        var called = false;
+        var o = py.PY_call(py.type('t', null, {
+            __abs__: function () {
+                called = true;
+                return py.None;
+            }
+        }));
+        expect(py.eval('abs(o)', {o: o})).to.be(null);
+        expect(called).to.be(true);
+    });
+    it('should TypeError on no generic method handler', function () {
+        var o = py.PY_call(py.type('t', null, {}));
+        expect(function () {
+            py.eval('abs(o)', {o: o});
+        }).to.throwException(/TypeError: bad operand type for abs\(\): 't'/);
+    });
+});

File test/builtins/float.js

View file
  • Ignore whitespace
             expect(py.eval('1 / 2')).to.be(0.5);
             expect(py.eval('2 / 1')).to.be(2);
         });
+        it('can yield its absolute value', function () {
+            expect(py.eval('abs(1)')).to.be(1);
+            expect(py.eval('abs(-1)')).to.be(1);
+            expect(py.eval('abs(1.5)')).to.be(1.5);
+            expect(py.eval('abs(-1.5)')).to.be(1.5);
+            expect(py.eval('abs(0)')).to.be(0);
+        });
     });
 });