rubypython 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/README.txt +1 -1
- data/ext/rubypython_bridge/extconf.rb +13 -5
- data/ext/rubypython_bridge/rp_object.c +154 -1
- data/ext/rubypython_bridge/rp_object.h +13 -0
- data/ext/rubypython_bridge/rtop.c +1 -0
- data/ext/rubypython_bridge/rubypython_bridge.c +2 -0
- data/lib/rubypython/version.rb +1 -1
- data/lib/rubypython/wrapper_extensions.rb +3 -1
- data/lib/rubypython.rb +44 -2
- data/test/test_rubypython.rb +46 -5
- data/test/test_session.rb +6 -0
- data/website/index.html +1 -1
- metadata +3 -2
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.2.4 2008-10-24
|
2
|
+
* Major Enhancements
|
3
|
+
* Provided setter methods for object attributes. Python object attributes can now be set
|
4
|
+
from within ruby.
|
5
|
+
* Made wrapper classes a subclass of custom made blank object class to try to avoid name
|
6
|
+
collisions.
|
7
|
+
* Bug Fix
|
8
|
+
* Changed part of extconf.rb file that would not work under windows.
|
1
9
|
== 0.2.3 2008-08-29
|
2
10
|
* 2 Major Enhancements
|
3
11
|
* Introduced PyMain object to as a singleton wrapper for the Python __main__ and
|
data/README.txt
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
RubyPython.stop
|
23
23
|
|
24
24
|
== REQUIREMENTS:
|
25
|
-
Python must be installed. Currently, RubyPython requires python2.5 but it may soon be able to work
|
25
|
+
Python must be installed. Currently, RubyPython requires python2.5 but it may soon be able to work with other versions. It should work fine with Python 2.4 but you may experience issues with lower versions. I have only tested it on Mac OSX 10.5, so I'm not sure what parts may need correcting for other systems.
|
26
26
|
|
27
27
|
== INSTALL:
|
28
28
|
|
@@ -1,13 +1,21 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
require 'open3'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
begin
|
5
|
+
if (Open3.popen3("python --version") { |i,o,e| e.read}.chomp.split[1].to_f < 2.4)
|
6
|
+
puts "I'm sorry you need at least Python 2.4 to use rubypython"
|
7
|
+
exit -1
|
8
|
+
end
|
9
|
+
rescue
|
10
|
+
puts "Could not check python version. Do you have Python 2.4 or higher? (y/n)"
|
11
|
+
if(gets.chomp == "n")
|
12
|
+
puts "Please install Python 2.4 or higher"
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
puts "Okay."
|
7
16
|
end
|
8
|
-
|
9
17
|
dir_config("rubypython_bridge")
|
10
|
-
if(
|
18
|
+
if(!system("which python-config"))
|
11
19
|
print "Can't configure with python_config"
|
12
20
|
exit -1
|
13
21
|
end
|
@@ -4,6 +4,30 @@
|
|
4
4
|
RUBY_EXTERN VALUE mRubyPythonBridge;
|
5
5
|
RUBY_EXTERN VALUE ePythonError;
|
6
6
|
|
7
|
+
VALUE cBlankObject;
|
8
|
+
|
9
|
+
// :nodoc:
|
10
|
+
VALUE blank_undef_if(VALUE mname,VALUE klass)
|
11
|
+
{
|
12
|
+
if(rb_funcall(mname,rb_intern("match"),1,rb_str_new2("(?:^__)|(?:\\?$)|(?:^send$)|(?:^class$)"))==Qnil)
|
13
|
+
{
|
14
|
+
rb_undef_method(klass,STR2CSTR(mname));
|
15
|
+
return Qtrue;
|
16
|
+
}
|
17
|
+
else
|
18
|
+
{
|
19
|
+
return Qfalse;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
// :nodoc:
|
24
|
+
VALUE blank_obj_prep(VALUE self)
|
25
|
+
{
|
26
|
+
VALUE instance_methods=rb_funcall(self,rb_intern("instance_methods"),0);
|
27
|
+
rb_iterate(rb_each,instance_methods,blank_undef_if,self);
|
28
|
+
return self;
|
29
|
+
}
|
30
|
+
|
7
31
|
VALUE cRubyPyObject;
|
8
32
|
VALUE cRubyPyModule;
|
9
33
|
VALUE cRubyPyClass;
|
@@ -132,6 +156,55 @@ VALUE rp_inst_from_instance(PyObject *pInst)
|
|
132
156
|
return rInst;
|
133
157
|
}
|
134
158
|
|
159
|
+
VALUE rp_inst_attr_set(VALUE self,VALUE args)
|
160
|
+
{
|
161
|
+
VALUE name,name_string,rClassDict,result,rInstDict;
|
162
|
+
VALUE ret;
|
163
|
+
int instance;
|
164
|
+
char *cname;
|
165
|
+
PObj *pClassDict,*pInstDict,*pDict;
|
166
|
+
PyObject *pName;
|
167
|
+
|
168
|
+
if(!rp_has_attr(self,rb_ary_entry(args,0)))
|
169
|
+
{
|
170
|
+
int argc;
|
171
|
+
|
172
|
+
VALUE *argv;
|
173
|
+
argc=RARRAY(args)->len;
|
174
|
+
argv=ALLOC_N(VALUE,argc);
|
175
|
+
MEMCPY(argv,RARRAY(args)->ptr,VALUE,argc);
|
176
|
+
return rb_call_super(argc,argv);
|
177
|
+
}
|
178
|
+
name=rb_ary_shift(args);
|
179
|
+
name_string=rb_funcall(name,rb_intern("to_s"),0);
|
180
|
+
cname=STR2CSTR(name_string);
|
181
|
+
rb_funcall(name_string,rb_intern("chop!"),0);
|
182
|
+
|
183
|
+
if(NUM2INT(rb_funcall(args,rb_intern("size"),0))==1)
|
184
|
+
{
|
185
|
+
args=rb_ary_entry(args,0);
|
186
|
+
}
|
187
|
+
|
188
|
+
rClassDict=rb_iv_get(self,"@pclassdict");
|
189
|
+
rInstDict=rb_iv_get(self,"@pinstdict");
|
190
|
+
Data_Get_Struct(rClassDict,PObj,pClassDict);
|
191
|
+
Data_Get_Struct(rInstDict,PObj,pInstDict);
|
192
|
+
pName=PyString_FromString(cname);
|
193
|
+
if(PyDict_Contains(pInstDict->pObject,pName))
|
194
|
+
{
|
195
|
+
pDict=pInstDict;
|
196
|
+
|
197
|
+
}
|
198
|
+
else
|
199
|
+
{
|
200
|
+
pDict=pClassDict;
|
201
|
+
|
202
|
+
}
|
203
|
+
Py_XDECREF(pName);
|
204
|
+
PyDict_SetItemString(pDict->pObject,STR2CSTR(name_string),rtop_obj(args,0));
|
205
|
+
return Qtrue;
|
206
|
+
}
|
207
|
+
|
135
208
|
//:nodoc:
|
136
209
|
VALUE rp_inst_delegate(VALUE self,VALUE args)
|
137
210
|
{
|
@@ -140,6 +213,11 @@ VALUE rp_inst_delegate(VALUE self,VALUE args)
|
|
140
213
|
char *cname;
|
141
214
|
PObj *pClassDict,*pInstDict;
|
142
215
|
PyObject *pCalled;
|
216
|
+
|
217
|
+
if(rp_equal(args))
|
218
|
+
{
|
219
|
+
return rp_inst_attr_set(self,args);
|
220
|
+
}
|
143
221
|
if(!rp_has_attr(self,rb_ary_entry(args,0)))
|
144
222
|
{
|
145
223
|
int argc;
|
@@ -201,6 +279,22 @@ VALUE rp_func_from_function(PyObject *pFunc)
|
|
201
279
|
return rFunc;
|
202
280
|
}
|
203
281
|
|
282
|
+
VALUE rp_obj_wrap(PyObject* pObj)
|
283
|
+
{
|
284
|
+
VALUE rObj;
|
285
|
+
if(PyFunction_Check(pObj)||PyMethod_Check(pObj)||!PyObject_HasAttrString(pObj,"__dict__"))
|
286
|
+
{
|
287
|
+
return rp_func_from_function(pObj);
|
288
|
+
|
289
|
+
}
|
290
|
+
if(PyInstance_Check(pObj))
|
291
|
+
{
|
292
|
+
rObj=rp_inst_from_instance(pObj);
|
293
|
+
return rObj;
|
294
|
+
}
|
295
|
+
return rp_cla_from_class(pObj);
|
296
|
+
}
|
297
|
+
|
204
298
|
VALUE rp_mod_call_func(VALUE self,VALUE func_name,VALUE args)
|
205
299
|
{
|
206
300
|
PObj *cself;
|
@@ -268,6 +362,50 @@ VALUE rp_obj_responds(VALUE self,VALUE mname)
|
|
268
362
|
}
|
269
363
|
return Qfalse;
|
270
364
|
}
|
365
|
+
|
366
|
+
int rp_equal(VALUE args)
|
367
|
+
{
|
368
|
+
VALUE mname=rb_ary_entry(args,0);
|
369
|
+
VALUE name_string=rb_funcall(mname,rb_intern("to_s"),0);
|
370
|
+
return Qtrue==rb_funcall(name_string,rb_intern("end_with?"),1,rb_str_new2("="));
|
371
|
+
}
|
372
|
+
|
373
|
+
|
374
|
+
|
375
|
+
int rp_double_bang(VALUE args)
|
376
|
+
{
|
377
|
+
VALUE mname=rb_ary_entry(args,0);
|
378
|
+
VALUE name_string=rb_funcall(mname,rb_intern("to_s"),0);
|
379
|
+
return Qtrue==rb_funcall(name_string,rb_intern("end_with?"),1,rb_str_new2("!!"));
|
380
|
+
}
|
381
|
+
|
382
|
+
VALUE rp_mod_attr_set(VALUE self,VALUE args)
|
383
|
+
{
|
384
|
+
VALUE rDict;
|
385
|
+
PObj *pDict;
|
386
|
+
VALUE mname=rb_ary_shift(args);
|
387
|
+
VALUE name_string=rb_funcall(mname,rb_intern("to_s"),0);
|
388
|
+
rb_funcall(name_string,rb_intern("chop!"),0);
|
389
|
+
if(!rp_has_attr(self,name_string))
|
390
|
+
{
|
391
|
+
int argc;
|
392
|
+
|
393
|
+
VALUE *argv;
|
394
|
+
argc=RARRAY(args)->len;
|
395
|
+
argv=ALLOC_N(VALUE,argc);
|
396
|
+
MEMCPY(argv,RARRAY(args)->ptr,VALUE,argc);
|
397
|
+
return rb_call_super(argc,argv);
|
398
|
+
}
|
399
|
+
if(NUM2INT(rb_funcall(args,rb_intern("size"),0))==1)
|
400
|
+
{
|
401
|
+
args=rb_ary_entry(args,0);
|
402
|
+
}
|
403
|
+
rDict=rb_iv_get(self,"@pdict");
|
404
|
+
Data_Get_Struct(rDict,PObj,pDict);
|
405
|
+
PyDict_SetItemString(pDict->pObject,STR2CSTR(name_string),rtop_obj(args,0));
|
406
|
+
return Qtrue;
|
407
|
+
}
|
408
|
+
|
271
409
|
//:nodoc:
|
272
410
|
VALUE rp_mod_delegate(VALUE self,VALUE args)
|
273
411
|
{
|
@@ -275,6 +413,14 @@ VALUE rp_mod_delegate(VALUE self,VALUE args)
|
|
275
413
|
VALUE ret;
|
276
414
|
PObj *pDict;
|
277
415
|
PyObject *pCalled;
|
416
|
+
if(rp_equal(args))
|
417
|
+
{
|
418
|
+
return rp_mod_attr_set(self,args);
|
419
|
+
}
|
420
|
+
// if(rp_double_bang)
|
421
|
+
// {
|
422
|
+
// return rp_mod_attr_db(args);
|
423
|
+
// }
|
278
424
|
if(!rp_has_attr(self,rb_ary_entry(args,0)))
|
279
425
|
{
|
280
426
|
int argc;
|
@@ -307,6 +453,13 @@ VALUE rp_mod_delegate(VALUE self,VALUE args)
|
|
307
453
|
|
308
454
|
}
|
309
455
|
|
456
|
+
// :nodoc:
|
457
|
+
inline void Init_BlankObject()
|
458
|
+
{
|
459
|
+
cBlankObject=rb_define_class_under(mRubyPythonBridge,"BlankObject",rb_cObject);
|
460
|
+
blank_obj_prep(cBlankObject);
|
461
|
+
}
|
462
|
+
|
310
463
|
/*
|
311
464
|
A wrapper class for Python objects that allows them to manipulated from within ruby.
|
312
465
|
|
@@ -316,7 +469,7 @@ classes which wrap Python objects of similar names.
|
|
316
469
|
*/
|
317
470
|
inline void Init_RubyPyObject()
|
318
471
|
{
|
319
|
-
cRubyPyObject=rb_define_class_under(mRubyPythonBridge,"RubyPyObject",
|
472
|
+
cRubyPyObject=rb_define_class_under(mRubyPythonBridge,"RubyPyObject",cBlankObject);
|
320
473
|
rb_define_alloc_func(cRubyPyObject,rp_obj_alloc);
|
321
474
|
rb_define_method(cRubyPyObject,"free_pobj",rp_obj_free_pobj,0);
|
322
475
|
rb_define_method(cRubyPyObject,"__name",rp_obj_name,0);
|
@@ -51,5 +51,18 @@ VALUE rp_cla_new_inst(VALUE self,VALUE args);
|
|
51
51
|
|
52
52
|
VALUE rp_obj_responds(VALUE self,VALUE mname);
|
53
53
|
|
54
|
+
VALUE blank_undef_if(VALUE mname,VALUE klass);
|
55
|
+
|
56
|
+
VALUE blank_obj_prep(VALUE self);
|
57
|
+
|
58
|
+
int rp_equal(VALUE args);
|
59
|
+
|
60
|
+
int rp_double_bang(VALUE args);
|
61
|
+
|
62
|
+
VALUE rp_mod_attr_set(VALUE self,VALUE args);
|
63
|
+
|
64
|
+
VALUE rp_inst_attr_set(VALUE self, VALUE args);
|
65
|
+
|
66
|
+
VALUE rp_obj_wrap(PyObject* pObj);
|
54
67
|
|
55
68
|
#endif /* _RP_OBJECT_H_ */
|
@@ -4,6 +4,7 @@ VALUE mRubyPythonBridge;
|
|
4
4
|
RUBY_EXTERN VALUE cRubyPyObject;
|
5
5
|
RUBY_EXTERN VALUE cRubyPyModule;
|
6
6
|
RUBY_EXTERN VALUE cRubyPyClass;
|
7
|
+
RUBY_EXTERN VALUE cBlankObject;
|
7
8
|
|
8
9
|
|
9
10
|
/*
|
@@ -84,6 +85,7 @@ void Init_rubypython_bridge()
|
|
84
85
|
rb_define_module_function(mRubyPythonBridge,"start",rp_start,0);
|
85
86
|
rb_define_module_function(mRubyPythonBridge,"stop",rp_stop,0);
|
86
87
|
rb_define_module_function(mRubyPythonBridge,"import",rp_import,1);
|
88
|
+
Init_BlankObject();
|
87
89
|
Init_RubyPyObject();
|
88
90
|
Init_RubyPyModule();
|
89
91
|
Init_RubyPyClass();
|
data/lib/rubypython/version.rb
CHANGED
@@ -15,8 +15,9 @@ end
|
|
15
15
|
# The PyMainClass object provides somewhat experimental block support.
|
16
16
|
# A block may be passed to a method call and the object returned by the function call
|
17
17
|
# will be passed as an argument to the block.
|
18
|
-
class PyMainClass
|
18
|
+
class PyMainClass < RubyPythonBridge::BlankObject
|
19
19
|
include Singleton
|
20
|
+
attr_writer :main, :builtin
|
20
21
|
#:nodoc:
|
21
22
|
def main
|
22
23
|
@main||=RubyPython.import "__main__"
|
@@ -41,6 +42,7 @@ class PyMainClass
|
|
41
42
|
if(block)
|
42
43
|
return block.call(result)
|
43
44
|
end
|
45
|
+
result
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
data/lib/rubypython.rb
CHANGED
@@ -30,6 +30,33 @@ This allows one to do something like the following:
|
|
30
30
|
puts cPickle.dumps "RubyPython is still awesome!"
|
31
31
|
end
|
32
32
|
|
33
|
+
The downside to the above method is that the block has no access to the encompassing scope. An
|
34
|
+
alternative is to use <tt>RubyPython.session</tt>. The downside to this approach is that the module
|
35
|
+
methods are not available by their unqualified names: i.e.
|
36
|
+
irb(main):001:0> RubyPython.session do
|
37
|
+
irb(main):002:1* cPickle=import "cPickle"
|
38
|
+
irb(main):003:1> end
|
39
|
+
NoMethodError: undefined method `import' for main:Object
|
40
|
+
from (irb):2
|
41
|
+
from ./rubypython.rb:93:in `call'
|
42
|
+
from ./rubypython.rb:93:in `session'
|
43
|
+
from (irb):1
|
44
|
+
|
45
|
+
However:
|
46
|
+
irb(main):001:0> RubyPython.session do
|
47
|
+
irb(main):002:1* cPickle=RubyPython.import "cPickle"
|
48
|
+
irb(main):003:1> puts cPickle.dumps "RubyPython is still awesome!"
|
49
|
+
irb(main):004:1> end
|
50
|
+
S'RubyPython is still awesome!'
|
51
|
+
.
|
52
|
+
=> nil
|
53
|
+
|
54
|
+
A compromise can be achieved by just including the RubyPython module into the scope you're
|
55
|
+
working in.
|
56
|
+
|
57
|
+
If you really wish to be free of dealing with the interpreter, just import 'rubypython/session'.
|
58
|
+
This will start the interpreter on import and will halt it when execution ends.
|
59
|
+
|
33
60
|
==Errors
|
34
61
|
The RubyPythonModule defines a new error object, PythonError. Should any error occur within
|
35
62
|
the Python interpreter, the class and value of the error will be passed back into ruby within
|
@@ -64,6 +91,8 @@ module RubyPython
|
|
64
91
|
ObjectSpace.each_object(RubyPythonBridge::RubyPyObject) do |o|
|
65
92
|
o.free_pobj
|
66
93
|
end
|
94
|
+
PyMain.main=nil
|
95
|
+
PyMain.builtin=nil
|
67
96
|
RubyPythonBridge.stop
|
68
97
|
end
|
69
98
|
|
@@ -74,11 +103,24 @@ module RubyPython
|
|
74
103
|
|
75
104
|
# Handles the setup and cleanup involved with using the interpreter for you.
|
76
105
|
# Note that all Python object will be effectively scope to within the block
|
77
|
-
# as the embedded interpreter will be halted at its end.
|
106
|
+
# as the embedded interpreter will be halted at its end. The supplied block is
|
107
|
+
# run within the scope of the RubyPython module.
|
108
|
+
#
|
109
|
+
# Alternatively the user may prefer RubyPython.session which simples handles
|
110
|
+
# initialization and cleanup of the interpreter.
|
78
111
|
def self.run(&block)
|
79
112
|
start
|
80
113
|
module_eval(&block)
|
81
114
|
stop
|
82
115
|
end
|
116
|
+
|
117
|
+
# Simply starts the interpreter, runs the supplied block, and stops the interpreter.
|
118
|
+
def self.session(&block)
|
119
|
+
start
|
120
|
+
retval=block.call
|
121
|
+
stop
|
122
|
+
return retval
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
83
126
|
|
84
|
-
end
|
data/test/test_rubypython.rb
CHANGED
@@ -5,13 +5,14 @@ class TestRubypython < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
end
|
7
7
|
|
8
|
+
|
8
9
|
def test_simple
|
9
10
|
assert RubyPython.start
|
10
11
|
assert RubyPython.import "urllib"
|
11
12
|
assert(RubyPython.stop)
|
12
13
|
assert(!RubyPython.stop)
|
13
14
|
end
|
14
|
-
|
15
|
+
|
15
16
|
def test_delegation
|
16
17
|
RubyPython.start
|
17
18
|
cPickle=RubyPython.import("cPickle")
|
@@ -29,14 +30,14 @@ class TestRubypython < Test::Unit::TestCase
|
|
29
30
|
end
|
30
31
|
assert(RubyPython.stop)
|
31
32
|
end
|
32
|
-
|
33
|
+
|
33
34
|
def test_two_imports
|
34
35
|
RubyPython.start
|
35
36
|
RubyPython.import "cPickle"
|
36
37
|
RubyPython.import "urllib"
|
37
38
|
RubyPython.stop
|
38
39
|
end
|
39
|
-
|
40
|
+
|
40
41
|
def test_propogate_python_errror
|
41
42
|
RubyPython.start
|
42
43
|
assert_raise PythonError do
|
@@ -44,7 +45,7 @@ class TestRubypython < Test::Unit::TestCase
|
|
44
45
|
end
|
45
46
|
RubyPython.stop
|
46
47
|
end
|
47
|
-
|
48
|
+
|
48
49
|
def test_run_method
|
49
50
|
unpickled=nil
|
50
51
|
RubyPython.run do
|
@@ -55,7 +56,7 @@ class TestRubypython < Test::Unit::TestCase
|
|
55
56
|
assert_equal(unpickled,{"a"=>"n", [1, "2"]=>4})
|
56
57
|
assert(!RubyPython.stop)
|
57
58
|
end
|
58
|
-
|
59
|
+
|
59
60
|
def test_instance_method_delegation
|
60
61
|
RubyPython.start
|
61
62
|
wave=RubyPython.import "wave"
|
@@ -64,4 +65,44 @@ class TestRubypython < Test::Unit::TestCase
|
|
64
65
|
w.close
|
65
66
|
RubyPython.stop
|
66
67
|
end
|
68
|
+
|
69
|
+
def test_pymain_delegation
|
70
|
+
RubyPython.start
|
71
|
+
assert_equal(PyMain.float(42),42.to_f)
|
72
|
+
RubyPython.stop
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_block_syntax
|
76
|
+
returned=""
|
77
|
+
RubyPython.start
|
78
|
+
returned=PyMain.float(22) do |f|
|
79
|
+
f*2
|
80
|
+
end
|
81
|
+
assert_equal(returned,44.0)
|
82
|
+
RubyPython.stop
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_session_function
|
86
|
+
RubyPython.session do
|
87
|
+
cPickle=RubyPython.import "cPickle"
|
88
|
+
cPickle.inspect
|
89
|
+
assert_equal(cPickle.loads("(dp1\nS'a'\nS'n'\ns(I1\nS'2'\ntp2\nI4\ns."),{"a"=>"n", [1, "2"]=>4})
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def test_setter_ary
|
95
|
+
RubyPython.session do
|
96
|
+
sys=RubyPython.import 'sys'
|
97
|
+
sys.path=[""]
|
98
|
+
assert_equal([""],sys.path)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_setter_instance
|
103
|
+
RubyPython.session do
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
67
108
|
end
|
data/website/index.html
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
<h1>rubypython</h1>
|
34
34
|
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/rubypython"; return false'>
|
35
35
|
<p>Get Version</p>
|
36
|
-
<a href="http://rubyforge.org/projects/rubypython" class="numbers">0.2.
|
36
|
+
<a href="http://rubyforge.org/projects/rubypython" class="numbers">0.2.4</a>
|
37
37
|
</div>
|
38
38
|
<h2>What</h2>
|
39
39
|
<p>RubyPython is a C bridge between Ruby and Python with a Ruby interface. It’s aim is to make the power of the Python’s great standard library available to Ruby programmers from within Ruby.<br />
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubypython
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Raines
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-10-24 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -113,3 +113,4 @@ test_files:
|
|
113
113
|
- test/test_helper.rb
|
114
114
|
- test/test_rubypython.rb
|
115
115
|
- test/test_rubypython_bridge_extn.rb
|
116
|
+
- test/test_session.rb
|