hdima / erlport Goto Github PK
View Code? Open in Web Editor NEWErlPort - connect Erlang to other languages
Home Page: http://erlport.org
License: BSD 3-Clause "New" or "Revised" License
ErlPort - connect Erlang to other languages
Home Page: http://erlport.org
License: BSD 3-Clause "New" or "Revised" License
For python:
I think we can use six library, it can come as git submodule
For ruby:
maybe drop 1.8 support, cause it old, and maintenance will end on July 31, 2014.
maybe even drop 1.9, and support current 2.0 and 2.1 (I'm not a ruby guy, but I hope it will be one code version for both)
Actually I implemented #15 for myself, and wanted to submit PR, but found frustrating that there is almost the same code for python2 and python3.
I could try to make one code for both python2 and python3.
I could also try to do smth with ruby.
What do you think?
Looking at docs it shows to use:
from erlport.erlang import call
but in the erlport/erlang.py call is not even exposed its inside def MessageHandler(object):
Maybe I am doing something wrong or the documentation is outdated?
EDIT: I kind of hacked together a cast which is really not a cast but a send using:
from erlport.erlterms import Atom
from erlport.erlproto import Port
port = Port()
port.write((Atom('M'), fromerlang_pid, "hi"))
Hi,
When I try to start erlport I get the following:
3> {ok, P} = python:start().
** exception error: no match of right hand side value
{error,{not_dir,"C:\WINDOWS\system32\python25.zip"}}
This is inconvenient. I am not a python expert, but I think it is not unusual to have zip files in PYTHONPATH.
Regards,
Willem
I understand that if I'm going to be writing Elixir, I might as well use its method for calling system methods. But in the interest of reusing existing Ruby scripts, the first thing I tried after "hello world" is to make a
system call with ruby.
I'm following the tutorial here: Ruby code in Elixir project
I have this elixir file:
defmodule RubyElixir.RubyCall do
use Export.Ruby
def ruby_call do
{:ok, ruby} = Ruby.start(
ruby_lib: Path.expand("lib/ruby")
)
ruby
|> Ruby.call("ruby", "ruby_v", [])
end
end
and this ruby file:
def ruby_v(name)
puts system "ruby -v"
end
With either system
or backticks, I get this error:
iex(1)> RubyElixir.RubyCall.ruby_call
** (ErlangError) erlang error: {:ruby, :IOError, "unsupported STDOUT operation for ErlPort", ["-e:1:in `<main>'", "/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'", "/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'", "/home/max/Elixir/ruby_elixir/_build/dev/lib/erlport/priv/ruby1.9/erlport/cli.rb:94:in `<top (required)>'", "/home/max/Elixir/ruby_elixir/_build/dev/lib/erlport/priv/ruby1.9/erlport/cli.rb:41:in `main'", "/home/max/Elixir/ruby_elixir/_build/dev/lib/erlport/priv/ruby1.9/erlport/erlang.rb:138:in `start'", "/home/max/Elixir/ruby_elixir/_build/dev/lib/erlport/priv/ruby1.9/erlport/erlang.rb:194:in `_receive'", "/home/max/Elixir/ruby_elixir/_build/dev/lib/erlport/priv/ruby1.9/erlport/erlang.rb:234:in `call_with_error_handler'", "/home/max/Elixir/ruby_elixir/_build/dev/lib/erlport/priv/ruby1.9/erlport/erlang.rb:195:in `block in _receive'", "/home/max/Elixir/ruby_elixir/_build/dev/lib/erlport/priv/ruby1.9/erlport/erlang.rb:221:in `incoming_call'", "/home/max/Elixir/ruby_elixir/lib/ruby/ruby.rb:2:in `ruby_v'", "/home/max/Elixir/ruby_elixir/lib/ruby/ruby.rb:2:in `system'", "/home/max/Elixir/ruby_elixir/_build/dev/lib/erlport/priv/ruby1.9/erlport/stdio.rb:59:in `method_missing'"]}
(erlport) src/erlport.erl:234: :erlport.call/3
I try:
{ok, P} = python:start().
Error:
/usr/bin/python: No module named erlport.cli
Where is erlport.cli ?
Tnx.
Changelog: https://docs.python.org/3.11/whatsnew/3.11.html#removed
Replacing getargspec
with getfullargspec
makes it work correctly, however it's behaviour changes on Python versions < 3.7 : https://docs.python.org/3.11/library/inspect.html#inspect.getfullargspec
While trying to call boto.s3.connection S3Connection.get_bucket method in Python3, realised that there is no way to pass in a str() in Python3.
Erlang Char lists (string) become lists in Python 3. Erlang binary becomes bytes in Python3. But using bytes.decode in python3 results in a char list being returned back to the Erlang VM, instead of an opaque object that represents a python3 str(). Thus there is no way to pass in a str object to a method call.
Looks like cd
option introduce some problems:
1> PyBaseDir = "/home/seriy/workspace/mediamon_crawl/priv/py_trash_filter",
1> Virtualenv = PyBaseDir ++ "/pyenv",
1> Python = Virtualenv ++ "/bin/python2.7".
2> python:start([{env, [{"VIRTUAL_ENV", Virtualenv}]},
{python, Python},
{python_path, [PyBaseDir]}]).
{ok,<0.355.0>}
3> python:start([{env, [{"VIRTUAL_ENV", Virtualenv}]},
{python, Python},
{python_path, [PyBaseDir]},
{cd, PyBaseDir}]).
{ok,<0.357.0>}
/home/seriy/workspace/mediamon_crawl/priv/py_trash_filter/pyenv/bin/python2.7: No module named erlport
Looks like this is because https://github.com/hdima/erlport/blob/1.0.0alpha/src/python_options.erl#L119 generate relative path. Maybe wrap it with filemane:absname/1
?
This error shows only in a compiled executive. When the same project is run in via $ iex -S mix
, iex> :python.start()
is successful. There is erlport/priv
in the deps
directory. What am I missing?
Since erlport cannot do variable assignments without pickling to erlang, I had met with the following problem:
http://stackoverflow.com/questions/7107409/why-does-pickle-protocol-2-let-me-serialise-an-open-file-object
iex(33)> fp = :python.call(pid, :'__builtin__', :'open', ["test.log", "r+"])
iex(34)> :python.call(pid, :'__builtin__', :'repr', [fp])
"<closed file '<uninitialized file>', mode '<uninitialized file>' at 0x106003ae0>"
It is suggested to switch from pickle to dill (https://github.com/uqfoundation/dill).
Dill can pickle the following standard types::
none, type, bool, int, long, float, complex, str, unicode,
tuple, list, dict, file, buffer, builtin,
both old and new style classes,
instances of old and new style classes,
set, frozenset, array, functions, exceptions
Dill can also pickle more 'exotic' standard types::
functions with yields, nested functions, lambdas
cell, method, unboundmethod, module, code, methodwrapper,
dictproxy, methoddescriptor, getsetdescriptor, memberdescriptor,
wrapperdescriptor, xrange, slice,
notimplemented, ellipsis, quit
I find this error when compiling erlport dependency on a Phoenix project.
===> Compiling erlport
===> Compiling src/erlport.erl failed
src/erlport.erl:392: erlang:get_stacktrace/0: deprecated; use the new try/catch syntax for retrieving the stack backtrace
** (Mix) Could not compile dependency :erlport, "/Users/username/.mix/rebar3 bare compile --paths "/Users/username/project/path/_build/dev/lib/*/ebin"" command failed. You can recompile this dependency with "mix deps.compile erlport", update it with "mix deps.update erlport" or clean it with "mix deps.clean erlport"
My Elixir version
Erlang/OTP 21 [erts-10.0.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
Elixir 1.6.6 (compiled with OTP 20)
When you use decorators for functions, responsible for handling messages, erlport doesn't invoke them when the message arrives:
def some_decorator(fn):
def _inner(self, x):
return fn(self, x + 1)
return _inner
class SomeProtocol(Protocol):
@some_decorator
def handle_msg(self, x):
return ...
Now, if you try to send a command {msg, X} from erlang, it will not be recognized.
If we look closely to these tables -> columns for erlang binary() to python bytes() for [python3 only] vise versa.
This basically means that arguments of str() data type in erlang sent over to python functions will be converted to bytes() in python3.
So for python functions expecting arguments of str() data type, do not forget to convert the received arguments back to python string before using.
# gist ref
decoded_args = locals()
for arg_name, arg_value in locals().items():
if isinstance(arg_value, bytes):
decoded_args[arg_name] = arg_value.decode("utf-8")
TypeError is a common error and the exception handling in erlproto.py that returns {error, function_clause} will hide errors that occur in the handler function. Short of explicitly checking arg count (IMO unnecessary), I think it would be preferable to let the exception propagate.
We are using erlport to communicate with pools of Python processes in a production system. Each Python OS process has a 1-1 gen_server that utilizes erlport for bi-directional communication between the erlang VM and Python. The problem we have is the pooler library we are using does brutal_kill on the gen_server when a pool is shutdown leading to an untrappable exit of the gen_server. So we do not have an opportunity in our gen_server to terminate the port and we end up leaking Python processes. One way to resolve this problem is to enhance erlport to monitor its callers and call port_close whenever the last caller associated with the port has been shutdown. Here is a solution that I have tested in our environment.
$ git diff
diff --git a/src/erlport.erl b/src/erlport.erl
index ffa0670..afdf85c 100644
--- a/src/erlport.erl
+++ b/src/erlport.erl
@@ -122,9 +122,10 @@ init(Fun) when is_function(Fun, 0) ->
%% @doc Synchronous event handler
%% @hidden
%%
-handle_call(Call={call, _M, _F, _A, Options}, From, State=#state{})
+handle_call(Call={call, _M, _F, _A, Options}, {Pid, _Ref} = From, State)
when is_list(Options) ->
- send_request(Call, From, Options, State);
+ NewState = monitor_caller(Pid, State),
+ send_request(Call, From, Options, NewState);
handle_call(Request, From, State) ->
Error = {unknown_call, ?MODULE, Request, From},
{reply, Error, State}.
@@ -160,6 +161,21 @@ handle_info({'EXIT', Pid, {Id, Result}}, State=#state{calls=Calls}) ->
error ->
{noreply, State}
end;
+handle_info({'DOWN', Ref, process, Pid, _Reason}, State=#state{port=Port, callers=Callers}) ->
+ case orddict:find(Pid, Callers) of
+ error ->
+ {noreply, State};
+ _ ->
+ erlang:demonitor(Ref),
+ Callers2 = orddict:erase(Pid, Callers),
+ case orddict:size(Callers2) of
+ 0 ->
+ port_close(Port),
+ {stop, shutdown, State#state{callers=Callers2}};
+ _ ->
+ {noreply, State#state{callers=Callers2}}
+ end
+ end;
handle_info({erlport_timeout, {in, Id}}, State=#state{calls=Calls}) ->
case orddict:find(Id, Calls) of
{ok, {Pid, _Timer}} ->
@@ -410,3 +426,15 @@ incoming_call(Id, Module, Function, Args, _Context, State=#state{
Calls2 = orddict:store(Id, Info, Calls),
{noreply, State#state{calls=Calls2}}
end.
+
+%%
+%% @doc Add monitor for caller
+%%
+monitor_caller(Pid, State=#state{callers = Callers}) ->
+ case orddict:find(Pid, Callers) of
+ error ->
+ Ref = erlang:monitor(process, Pid),
+ State#state{callers=orddict:store(Pid, Ref, Callers)};
+ _ ->
+ State
+ end.
diff --git a/src/erlport.hrl b/src/erlport.hrl
index d3eb01b..0d4bad7 100644
--- a/src/erlport.hrl
+++ b/src/erlport.hrl
@@ -40,7 +40,9 @@
% orddict(): CallId -> {From::term(), Timer::reference() | undefined}
sent = orddict:new() :: list(),
% orddict(): CallId -> {Pid::pid(), Timer::reference()}
- calls = orddict:new() :: list()
+ calls = orddict:new() :: list(),
+ % orddict(): CallerId -> Monitor::reference()
+ callers = orddict:new() :: list()
}).
-endif. % ERLPORT_HRL
Hi Dev team and All,
I tried pip install erlport.erlterms. It didn't work. Then I downloaded the binary package and tried python -m pip install . There came an error saying not found setup.py. How can I have erlport.erlterms installed?
Help please! Thank you very much.
Best regards,
Jianliang
Hi,
In windows's path have spaces, so open_port cannot work.
Can be solved in the following way:
Fullname = have_blank(filename:nativename(filename:absname(Filename))) % in erlport_options 252
have_spaces(FullName) ->
case string:str(FullName, " ") of
0 -> FullName;
_ -> """ ++ FullName ++ """
end.
Hope useful.
I made a repo demonstrating example usage with Python in Elixir here: (https://github.com/arthurcolle/elixir-snake)
When I redownload it, run mix deps.get
, then iex -S mix
, in the interpreter, running this should work:
just like how
iex(2)> {:ok, python} = :python.start()
returns:
{:ok, #PID<0.90.0>}
the ruby version should also work, however running this:
iex(2)> {:ok, ruby} = :ruby.start()
{:ok, #PID<0.95.0>}
iex(3)> /Users/arthur/.rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in require': cannot load such file -- erlport/cli (LoadError) from /Users/arthur/.rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in
require'
from -e:1:in `
23:42:43.046 [error] GenServer #PID<0.95.0> terminating
Last message: {#Port<0.3940>, {:exit_status, 1}}
State: {:state, :infinity, 0, #Port<0.3940>, [], []}
** (stop) {:port_closed, {:code, 1}}
What exactly is the problem? Is there a gem that I need to download and install in order to make it work? I couldn't find this in the docs, but I'd appreciate any pointers. Thanks!
Hello,
I'm trying to run 'Send messages from Erlang to Python example', but I get the following errors in Erlang shell:
exception error: {python,'builtins.TypeError',"bytes object expected",
[{"c:\Program Files\erl7.2.1\lib\erlport-0.9.8\priv\python3\erlport\erlterms.py",
66,"new",
"raise TypeError("bytes object expected")"},
{"C:\Users\estjako\PycharmProjects\untitled\handler.py",
8,"register_handler","return Atom("ok")"},
{"c:\Program Files\erl7.2.1\lib\erlport-0.9.8\priv\python3\erlport\erlang.py",
239,"_incoming_call",
"result = Atom(b"r"), mid, self.encoder(f(_map(self.decoder, args)))"},
{"c:\Program Files\erl7.2.1\lib\erlport-0.9.8\priv\python3\erlport\erlang.py",
244,"_call_with_error_handler",
"function(_args)"}]}
in function erlport:call/3 (src/erlport.erl, line 234)
I'm currently using Erlang/OTP 18 and Python 3.4.
Thank you!
I use custom type for parsing ruby ast.
I found that negative integer array of ruby is encoded as erlang string of incorrect value.
def single_neg
[-1]
end
def two_neg
[-1,-2]
end
def single_pos
[1]
end
def two_pos
[1,2]
end
calling these functions generate incorrect value
1> {ok, R} = ruby:start().
{ok,<0.35.0>}
2> ruby:call(R, './int_ary.rb', single_neg, []).
"ÿ"
3> ruby:call(R, './int_ary.rb', two_neg, []).
"ÿþ"
4> ruby:call(R, './int_ary.rb', single_pos, []).
[1]
5> ruby:call(R, './int_ary.rb', two_pos, []).
[1,2]
the real problem is that it's not a char with proper value
6> Tn = ruby:call(R, './int_ary.rb', two_neg, []).
"ÿþ"
9> lists:nth(1,Tn).
255
10> lists:nth(2,Tn).
254
which should be -1 and -2, not 255 and 254.
Hi.
I might be wrong as I've spent just several minutes looking into code.
But the code of Port._read_data() seems to have a problem. It may read excess data.
The better way to explain the problem is to show the patch that should resolve it:
--- erlproto.py 2011-04-17 16:21:42.000000000 +0400 +++ erlproto.new.py 2011-04-17 16:21:54.000000000 +0400 @@ -103,7 +103,7 @@ data = "" while len(data) != length: try: - buf = os.read(self.in_d, length) + buf = os.read(self.in_d, length - len(data)) except IOError, why: if why.errno == errno.EPIPE: raise EOFError()
and the rest of the function:
data += nuf return data
As you can see, if we have read not enough data we would go once again into the loop.
But we will try to read again the whole chunk with length "length" while we should read the remain part "length - len(data)"
Ivan.
Using Windows XP and Windows 7, Erlport can't find the python interpreter.
It works without a hitch in Debian.
I've tried various combinations of environment variables and python:start() options.
python:start([{python, "C:\Python27"}]). and all combinations of that directory.
error: invalid_option, {python, "c:\Python27"}, not_found}}
If I add a \ to the end of that path, it just sits there doing nothing forever until I kill the process.
It looks like ERLPORT_PYTHON is the correct env var to set the interpreter path,
{error,{invalid_env_var,{"ERLPORT_PYTHON","c:\python27"), not_found}}
After messing around with it for a while, I found the root cause is a missing python entry in the PATH env var. after adding the python folder to PATH, I was able to run erlport without setting any options.
Just posting this info for other people who may have this problem.
I am guessing that this is erlang code. It does not look like python code :)
http://erlport.org/docs/python.html#send-messages-from-python-to-erlang
I was looking for this particular stuff so now I'm a bit stumped on where to start.
Our sysadmin tried to install erlport. We're running RH Rel 6 (Erlang 17.3-2.el6 rpm). Since binary releases were only available for versions up to 16, he tried to build from source.
He reports:
am getting quite a few build errors terminating with:
./release bin
./release: line 14: erlport-Erlang/OTP 17 [erts-6.2] [source-5c974be]
[64-bit] [smp:4:4] [async-threads:10] [hipe]
[kernel-poll:false]-0.9.8.tar.gz: No such file or directory
make: *** [release] Error 1
An issue running this with erlang v17 perhaps?
Is there a version 17 binary release somewhere?
Thanks!
-Mark
The described Python API is not available:
>>> import erlport.erlang
>>> erlport.erlang.call
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'call'
When attempting to start a named python client the following exception is thrown.
no function clause matching gen:where(Name)
It is possibly the fact that this library is not necessarily supported under erlang/OTP-20.
My version is:
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [kernel-poll:false]
Running Erlport through Elixir, I was getting this error, and it was driving me nuts:
/usr/bin/python: No module named erlport.cli
** (ErlangError) erlang error: {:port_closed, {:code, 1}}
17:46:26.849 [error] GenServer #PID<0.128.0> terminating
** (stop) {:port_closed, {:code, 1}}
Last message: {#Port<0.5453>, {:exit_status, 1}}
State: {:state, :infinity, 0, #Port<0.5453>, [{1, {{#PID<0.126.0>, #Reference<0.0.5.24>}, :undefined}}], []}
src/erlport.erl:234: :erlport.call/3
The way I resolved the issue was through:
pip uninstall erlport
Indeed, it seems that the erlport python module used internally is called "erlport", and so is the module used by external scripts, which caused this issue.
Perhaps consider renaming the internal python module?
NOTE: an issue about this seems to have been raised before here #6
I used erlport as a git dependency in the rebar.config of my erlang project.
Running my project throws an error when erlport tries to open python3.
Turns out python3 only works after the erlport project has been built with the makefile.
For the moment I have to enter the directory of erlport (which is a dependency of my project) and manually run 'make'.
Alternatively I could script it in my root project to build the dependency, but this solution smells.
Building erlport can be automatized by rebar3, by adding a pre-compile hook in the rebar3.config.
This way all rebar3 projects that use erlport as a dependency will build it automatically after fetching it from git.
I am getting this error compiling erlport:
** (Mix) Could not compile dependency :erlport, "/Users/mladen/.mix/rebar3 bare compile --paths "/Users/mladen/work/inSITE_Search/_build/dev/lib/*/ebin"" command failed.
I am getting this on a freshly installed macOS high sierra, while i never got this error on other Macs i tried.
I only found one user with similar error but without much luck fixing it:
https://translate.google.com/translate?sl=auto&tl=en&js=y&prev=_t&hl=sr&ie=UTF-8&u=https%3A%2F%2Fteratail.com%2Fquestions%2F114967&edit-text=&act=url
I am using default python that comes with mac (2.7) and i don't have erlport installed via pip.
I also don't get any error besides "could not compile" or i don't know here to look for error logs.
I am passing data between erlang and python using erlport, following the example here:
http://erlport.org/docs/python.html
the python file I'm calling only contains the line:
import pandas as pd
I am getting the error:
** exception error: {python,'exceptions.AttributeError',
"'function' object has no attribute 'lower'",
[{<<"/anaconda/lib/python2.7/site-packages/pandas/core/format.py">>,
1701,<<"detect_console_encoding">>,
<<"if not encoding or 'ascii' in encoding.lower(): # try again for something bette"...>>},
{<<"/anaconda/lib/python2.7/site-packages/pandas/core/config_init.py">>,
234,<<"<module>">>,
<<"cf.register_option('encoding', detect_console_encoding(), pc_encoding_doc,">>},
{<<"/anaconda/lib/python2.7/site-packages/pandas/__init__.py">>,
25,<<"<module>">>,<<"import pandas.core.config_init">>},
{<<"/Documents/data-algorithms/Alg"...>>,
3,<<"<module>">>,<<"import pandas as pd">>},
{<<"/Documents/testki"...>>,
237,<<"_incoming_call">>,
<<"f = __import__(module, {}, {}, [objects[0]])">>},
{<<"/Documents/te"...>>,
245,<<"_call_with_error_handler">>,<<"function(*args)">>}]}
in function erlport:call/3 (src/erlport.erl, line 234)
in call from algo_tester:start/0 (src/algo_tester.erl, line 27)
I can get rid of the error by commenting out the following two lines in /anaconda/lib/python2.7/site-packages/pandas/core/config_init.py
:
234 cf.register_option('encoding', detect_console_encoding(), pc_encoding_doc,
235 validator=is_text)
but then print
doesn't work any longer.
Has anyone encountered this before?
In this code:
try:
response = handler(*args)
except:
response = self._handle_error(sys.exc_info())
port.write(response)
if handler() attempts a port.write() which fails with EOFError (originally OSError(EPIPE)), the exception catch will craft a reponse for it which will cause another attempt to port.write() to the same, closed fifo.
Essentially, the EOFError() should not be caught by the try block
The website says 1.0 alpha is out, but that's an old version. We've been using erlport pretty happily in production for over a year, and I think you should release a new version.
Also, the rubygems and pypi packages should be updated. Pypi is at 0.6, which is very old.
> pip3 install erlport
Collecting erlport
Installing collected packages: erlport
Successfully installed erlport-0.6
> python3
Python 3.5.2 (default, Nov 25 2016, 16:29:04)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import erlport
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.5/site-packages/erlport/__init__.py", line 33, in <module>
from erlport.erlproto import Port, Protocol
File "/usr/local/lib/python3.5/site-packages/erlport/erlproto.py", line 107
except IOError, why:
^
SyntaxError: invalid syntax
I noticed the current version of erlport is 0.9.8 but the one listed on pypi is 0.6. Is this correct? If not, can the python package on pypi be updated?
Tangentially, how does one go about installing the erlport python library from the source code?
when i start my erlang node, the erl log print:
{error,{invalid python, "/usr/local/bin/python"}}
I'm following this guid to play the python demo:
http://erlport.org/docs/python.html#call-erlang-functions-from-python
When I call python from erlang then got some error:
(sonic@node1)3> python:call(P, pids, pids, []).
18:47:56.046 [error] CRASH REPORT Process <0.260.0> with 0 neighbours exited with reason: {1,{ok,<0.260.0>}} in erlport:'-spawn_call/4-fun-0-'/4 line 249
18:47:56.048 [error] CRASH REPORT Process <0.261.0> with 0 neighbours exited with reason: {1,{ok,<0.261.0>}} in erlport:'-spawn_call/4-fun-0-'/4 line 249
[<0.260.0>,<0.261.0>]
looks like those error does not affect the results, but I'm no idea where were wrong.
anyone can help? thanks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.