Giter Club home page Giter Club logo

aioupnp's Introduction

[Build Status codecov PyPI version Python 3.6 Python 3.7 Python 3.8

UPnP for asyncio

aioupnp is a python 3.6-8 library and command line tool to interact with UPnP gateways using asyncio. aioupnp requires the netifaces and defusedxml modules.

Supported devices

img

Installation

Verify python is version 3.6-8

python --version

Installation for normal usage

pip install aioupnp

Installation for development

git clone https://github.com/lbryio/aioupnp.git
cd aioupnp
pip install -e .

Usage

aioupnp [-h] [--debug_logging] [--interface=<interface>] [--gateway_address=<gateway_address>]
        [--lan_address=<lan_address>] [--timeout=<timeout>]
        [(--<case sensitive m-search header>=<value>)...]
        command [--<arg name>=<arg>]...

Commands

  • help
  • get_external_ip
  • m_search
  • add_port_mapping
  • get_port_mapping_by_index
  • get_redirects
  • get_specific_port_mapping
  • delete_port_mapping
  • get_next_mapping
  • gather_debug_info

To get the documentation for a command

aioupnp help get_external_ip

To get the external ip address

aioupnp get_external_ip

To list the active port mappings on the gateway

aioupnp get_redirects

To set up a TCP port mapping

aioupnp add_port_mapping --external_port=1234 --internal_port=1234 --lan_address=<lan_addr> --description=test --protocol=TCP

To delete a TCP port mapping

aioupnp delete_port_mapping --external_port=1234 --protocol=TCP

M-Search headers

UPnP uses a multicast protocol (SSDP) to locate the gateway. Gateway discovery is automatic by default, but you may provide specific headers for the search to use to override automatic discovery.

If m-search headers are provided as keyword arguments then all of the headers to be used must be provided, in the order they are to be used. For example:

aioupnp --HOST=239.255.255.250:1900 --MAN=\"ssdp:discover\" --MX=1 --ST=upnp:rootdevice m_search

Using non-default network interfaces

By default, the network device will be automatically discovered. The interface may instead be specified with the --interface, provided before the command to be run. The gateway used on the interface network may be specified with the --gateway_address argument.

aioupnp --interface=wlp4s0 --gateway_address=192.168.1.6 m_search

Example usage from python

from aioupnp.upnp import UPnP

async def main():
    upnp = await UPnP.discover()
    print(await upnp.get_external_ip())
    print(await upnp.get_redirects())

    print("adding a port mapping")
    await upnp.add_port_mapping(1234, 'TCP', 1234, upnp.lan_address, 'test mapping')
    print(await upnp.get_redirects())

    print("deleting the port mapping")
    await upnp.delete_port_mapping(1234, 'TCP')
    print(await upnp.get_redirects())


asyncio.run(main())

Troubleshooting

If aioupnp is failing with m-search timeouts this means the UPnP gateway (the router) isn't being found at all. To see if this error is expected try running m_search with debug logging, which will print out the packets sent and received:

aioupnp --debug_logging m_search

If you only see packets being sent or the replies are only from devices that aren't your router (smart devices, speakers, etc), then there are three options:

  • your router does not support UPnP (this is unlikely)
  • UPnP is turned off in the web gui for your router (more likely)
  • aioupnp has a bug (very likely if you don't see your router manufacturer doing well in the supported devices table)

If you see replies from the router but it still fails, then it's likely a bug in aioupnp.

If there are no replies and UPnP is certainly turned on, then a local firewall is the likely culprit.

Sending a bug report

If it still doesn't work, you can send a bug report using an included script. This script will try finding the UPnP gateway using aioupnp as well as miniupnpc and then try add and remove a port mapping using each library. The script does this while capturing the packets sent/received, which makes figuring out what went wrong possible. The script will create a file with this packet capture (aioupnp-bug-report.json) and automatically send it.

Note: the bug report script currently does not work on MacOS

git clone https://github.com/lbryio/aioupnp.git
cd aioupnp
python3 -m pip install -e .

python3 -m pip install --user certifi aiohttp miniupnpc
sudo -E python3 generate_bug_report.py

License

This project is MIT licensed. For the full license, see LICENSE.

Contact

The primary contact for this project is @jackrobison

aioupnp's People

Contributors

hackrush01 avatar jackrobison avatar philipphomann avatar strikerrus avatar tzarebczan avatar ykris45 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aioupnp's Issues

Fix SSDP unable to decode a datagram

2019-01-02 17:37:14,227 ERROR    aioupnp.protocols.ssdp:81: failed to decode SSDP packet
from 192.168.0.1:1900 (missing required field usn):
b'485454502f312e3120323030204f4b0d0a53543a75726e3a64736c666f72756d2d6f72673a736572766
63653a4d616e6167656d656e745365727665723a310d0a4558543a0d0a5345525645523a2043757374
f6d2f312e302055506e502f312e302050726f632f5665720d0a55534e3a757569643a757569643a66333
33386532342d663034652d313165382d383733302d3130356630363333626365300a3a3a75726e3a64
36c666f72756d2d6f72673a736572766963653a4d616e6167656d656e745365727665723a310d0a4341
348452d434f4e54524f4c3a6d61782d6167653d313230393630300d0a4c4f434154494f4e3a206874747
3a2f2f3139322e3136382e302e313a34393433312f646576696365646573632e786d6c0d0a0d0a'

UPnP configuration of port mapping do not work with router Sagemcom F@st3890V3

I get the message aioupnp encountered an error: M-SEARCH for 192.168.0.1:1900 timed out by aioupnp not beeing able to find the correct service definition file (scpd xml file).

This is the debug output I get from aioupnp --debug-logging gather_debug_info: debug-output.txt.

This is the response from http://192.168.0.1:49152/XXX/gatedesc0b.xml: gatedesc0b.xml.txt.
This has a <deviceList> inside <device> inside another <deviceList>. Do aioupnp support finding services inside nested deviceLists?

This is the response from http://192.168.0.1:49152/XXX/wanipconnSCPD.xml: wanipconnSCPD.xml.txt.
Which is the correct scpd-file.

I have replaced all UUID and other hex-numbers strings with X-es.

Prevents startup! KeyError: 'errorDescription'

2019-04-28 17:12:02,280 INFO     lbrynet.extras.daemon.Components:510: got external ip from UPnP: 192.168.1.2
2019-04-28 17:12:02,281 INFO     lbrynet.extras.daemon.Components:524: add UPnP port mappings
2019-04-28 17:12:02,318 ERROR    lbrynet.extras.daemon.Component:61: Error setting up upnp
Traceback (most recent call last):
  File "lbrynet\extras\daemon\Component.py", line 55, in _setup
  File "lbrynet\extras\daemon\Components.py", line 570, in start
  File "lbrynet\extras\daemon\Components.py", line 530, in _maintain_redirects
  File "lbrynet\extras\daemon\Components.py", line 50, in gather_dict
  File "lbrynet\extras\daemon\Components.py", line 48, in wait_value
  File "site-packages\aioupnp\upnp.py", line 167, in get_next_mapping
  File "site-packages\aioupnp\upnp.py", line 109, in _get_port_mapping_by_index
  File "site-packages\aioupnp\commands.py", line 47, in __call__
  File "site-packages\aioupnp\protocols\scpd.py", line 141, in scpd_post
  File "site-packages\aioupnp\serialization\soap.py", line 54, in deserialize_soap_post_response
  File "site-packages\aioupnp\fault.py", line 12, in handle_fault
KeyError: 'errorDescription'
2019-04-28 17:12:02,393 INFO     torba.client.basenetwork:88: ```

Invalid continuation byte

future: <Task finished coro=<Gateway._discover_gateway() done, defined at site-packages\aioupnp\gateway.py:157> exception=UnicodeDecodeError('utf-8', b'<?xml version="1.0"?>\r\n<root xmlns="urn:schemas-upnp-org:device-1-0"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType><friendlyName>ARRIS TG2492LG-85 Router</friendlyName><manufacturer>ARRIS</manufacturer><manufacturerURL>http://www.arrisi.com/</manufacturerURL><modelDescription>ARRIS TG2492LG-85 Router</modelDescription><modelName>ARRIS TG2492LG-85 Router</modelName><modelNumber>\xf0_\xf0</modelNumber><modelURL>http://www.arrisi.com/</modelURL><serialNumber>AA1P80581207</serialNumber><UDN>uuid:30353831-3230-3700-0000-48d34396e685</UDN><serviceList><service><serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType><serviceId>urn:upnp-org:serviceId:Layer3Forwarding1</serviceId><controlURL>/ctl/L3F</controlURL><eventSubURL>/evt/L3F</eventSubURL><SCPDURL>/L3F.xml</SCPDURL></service></serviceList><deviceList><device><deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType><friendlyName>WANDevice</friendlyName><manufacturer>ARRIS</manufacturer><manufacturerURL>http://www.arrisi.com/</manufacturerURL><modelDescription>WANDevice</modelDescription><modelName>WANDevice</modelName><modelNumber>20180122</modelNumber><modelURL>http://www.arrisi.com/</modelURL><serialNumber>AA1P80581207</serialNumber><UDN>uuid:30353831-3230-3700-0000-48d34396e685</UDN><UPC>TG2492LG-85</UPC><serviceList><service><serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType><serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId><controlURL>/ctl/CmnIfCfg</controlURL><eventSubURL>/evt/CmnIfCfg</eventSubURL><SCPDURL>/WANCfg.xml</SCPDURL></service></serviceList><deviceList><device><deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType><friendlyName>WANConnectionDevice</friendlyName><manufacturer>ARRIS</manufacturer><manufacturerURL>http://www.arrisi.com/</manufacturerURL><modelDescription>Residential Gateway</modelDescription><modelName>TG2492LG-85</modelName><modelNumber>20180122</modelNumber><modelURL>http://www.arrisi.com/</modelURL><serialNumber>AA1P80581207</serialNumber><UDN>uuid:30353831-3230-3700-0000-48d34396e685</UDN><UPC>TG2492LG-85</UPC><serviceList><service><serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType><serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId><controlURL>/ctl/IPConn</controlURL><eventSubURL>/evt/IPConn</eventSubURL><SCPDURL>/WANIPCn.xml</SCPDURL></service></serviceList></device></deviceList></device></deviceList><presentationURL>http://192.168.0.1/</presentationURL></device></root>', 477, 478, 'invalid continuation byte')>
Traceback (most recent call last):
  File "site-packages\aioupnp\gateway.py", line 177, in _discover_gateway
  File "site-packages\aioupnp\gateway.py", line 214, in discover_commands
  File "site-packages\aioupnp\protocols\scpd.py", line 114, in scpd_get
  File "site-packages\aioupnp\serialization\scpd.py", line 45, in deserialize_scpd_get_response

mappings lost if another SDK starts up on same ports

If you are running an SDK with upnp and another SDK starts up and doesn't detect that properly, the ports are torn down somehow. This has happened to me a couple times but I don't have the exact scenario yet, but think it's a failure case when it doesn't detect the existing mappings.

Failure to execute on Python 3.10: loop parameter was removed from asyncio.wait_for

$ aioupnp get_external_ip
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<Gateway._discover_gateway() done, defined at /media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/gateway.py:197> exception=UPnPError(TypeError('As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary'))>
Traceback (most recent call last):
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/protocols/ssdp.py", line 141, in listen_ssdp
    listen_result: Tuple[asyncio.BaseTransport, asyncio.BaseProtocol] = await loop.create_datagram_endpoint(
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1373, in create_datagram_endpoint
    protocol = protocol_factory()
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/protocols/ssdp.py", line 142, in <lambda>
    lambda: SSDPProtocol(SSDP_IP_ADDRESS, lan_address), sock=sock
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/protocols/ssdp.py", line 34, in __init__
    self.connected = asyncio.Event(loop=self.loop)
  File "/usr/lib/python3.10/asyncio/locks.py", line 168, in __init__
    super().__init__(loop=loop)
  File "/usr/lib/python3.10/asyncio/mixins.py", line 17, in __init__
    raise TypeError(
TypeError: As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/gateway.py", line 201, in _discover_gateway
    ssdp_proto = await multi_m_search(
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/protocols/ssdp.py", line 171, in multi_m_search
    protocol, gateway_address, lan_address = await listen_ssdp(
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/protocols/ssdp.py", line 147, in listen_ssdp
    raise UPnPError(err)
aioupnp.fault.UPnPError: As of 3.10, the *loop* parameter was removed from Event() since it is no longer necessary
Traceback (most recent call last):
  File "/media/Paulo/home//.local/bin/aioupnp", line 8, in <module>
    sys.exit(main())
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/__main__.py", line 117, in main
    run_cli(
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/upnp.py", line 434, in run_cli
    loop.run_until_complete(wrapper())
  File "/usr/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
    return future.result()
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/upnp.py", line 410, in wrapper
    u = await UPnP.discover(
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/upnp.py", line 56, in discover
    gateway = await Gateway.discover_gateway(
  File "/media/Paulo/home/.local/lib/python3.10/site-packages/aioupnp/gateway.py", line 225, in discover_gateway
    return await asyncio.wait_for(loop.create_task(
TypeError: wait_for() got an unexpected keyword argument 'loop'

3 UPNP issues spotted recently

These pile up on the logs since it re-requests upnp.

  1. 2019-01-02 17:37:14,227 ERROR aioupnp.protocols.ssdp:81: failed to decode SSDP packet from 192.168.0.1:1900 (missing required field usn): b'485454502f312e3120323030204f4b0d0a53543a75726e3a64736c666f72756d2d6f72673a736572766963653a4d616e6167656d656e745365727665723a310d0a4558543a0d0a5345525645523a20437573746f6d2f312e302055506e502f312e302050726f632f5665720d0a55534e3a757569643a757569643a66333033386532342d663034652d313165382d383733302d3130356630363333626365300a3a3a75726e3a64736c666f72756d2d6f72673a736572766963653a4d616e6167656d656e745365727665723a310d0a43414348452d434f4e54524f4c3a6d61782d6167653d313230393630300d0a4c4f434154494f4e3a20687474703a2f2f3139322e3136382e302e313a34393433312f646576696365646573632e786d6c0d0a0d0a'

future: <Task finished coro=<Gateway._discover_gateway() done, defined at site-packages\aioupnp\gateway.py:157> exception=UnicodeDecodeError('utf-8', b'<?xml version="1.0"?>\r\n<root xmlns="urn:schemas-upnp-org:device-1-0"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType><friendlyName>ARRIS TG2492LG-85 Router</friendlyName><manufacturer>ARRIS</manufacturer><manufacturerURL>http://www.arrisi.com/</manufacturerURL><modelDescription>ARRIS TG2492LG-85 Router</modelDescription><modelName>ARRIS TG2492LG-85 Router</modelName><modelNumber>\xf0_\xf0</modelNumber><modelURL>http://www.arrisi.com/</modelURL><serialNumber>AA1P80581207</serialNumber><UDN>uuid:30353831-3230-3700-0000-48d34396e685</UDN><serviceList><service><serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType><serviceId>urn:upnp-org:serviceId:Layer3Forwarding1</serviceId><controlURL>/ctl/L3F</controlURL><eventSubURL>/evt/L3F</eventSubURL><SCPDURL>/L3F.xml</SCPDURL></service></serviceList><deviceList><device><deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType><friendlyName>WANDevice</friendlyName><manufacturer>ARRIS</manufacturer><manufacturerURL>http://www.arrisi.com/</manufacturerURL><modelDescription>WANDevice</modelDescription><modelName>WANDevice</modelName><modelNumber>20180122</modelNumber><modelURL>http://www.arrisi.com/</modelURL><serialNumber>AA1P80581207</serialNumber><UDN>uuid:30353831-3230-3700-0000-48d34396e685</UDN><UPC>TG2492LG-85</UPC><serviceList><service><serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType><serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId><controlURL>/ctl/CmnIfCfg</controlURL><eventSubURL>/evt/CmnIfCfg</eventSubURL><SCPDURL>/WANCfg.xml</SCPDURL></service></serviceList><deviceList><device><deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType><friendlyName>WANConnectionDevice</friendlyName><manufacturer>ARRIS</manufacturer><manufacturerURL>http://www.arrisi.com/</manufacturerURL><modelDescription>Residential Gateway</modelDescription><modelName>TG2492LG-85</modelName><modelNumber>20180122</modelNumber><modelURL>http://www.arrisi.com/</modelURL><serialNumber>AA1P80581207</serialNumber><UDN>uuid:30353831-3230-3700-0000-48d34396e685</UDN><UPC>TG2492LG-85</UPC><serviceList><service><serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType><serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId><controlURL>/ctl/IPConn</controlURL><eventSubURL>/evt/IPConn</eventSubURL><SCPDURL>/WANIPCn.xml</SCPDURL></service></serviceList></device></deviceList></device></deviceList><presentationURL>http://192.168.0.1/</presentationURL></device></root>', 477, 478, 'invalid continuation byte')>
Traceback (most recent call last):
  File "site-packages\aioupnp\gateway.py", line 177, in _discover_gateway
  File "site-packages\aioupnp\gateway.py", line 214, in discover_commands
  File "site-packages\aioupnp\protocols\scpd.py", line 114, in scpd_get
  File "site-packages\aioupnp\serialization\scpd.py", line 45, in deserialize_scpd_get_response
Traceback (most recent call last):
  File "site-packages\aioupnp\gateway.py", line 169, in _discover_gateway
  File "site-packages\aioupnp\protocols\ssdp.py", line 173, in fuzzy_m_search
  File "site-packages\aioupnp\protocols\ssdp.py", line 166, in _fuzzy_m_search
aioupnp.fault.UPnPError: M-SEARCH for 192.168.1.128:1900 timed out
2019-01-03 19:39:43,329 ERROR    asyncio:1597: Task exception was never retrieved
future: <Task finished coro=<Gateway._discover_gateway() done, defined at site-packages\aioupnp\gateway.py:157> exception=UPnPError('M-SEARCH for 192.168.1.128:1900 timed out')>
Traceback (most recent call last):
  File "site-packages\aioupnp\gateway.py", line 169, in _discover_gateway
  File "site-packages\aioupnp\protocols\ssdp.py", line 173, in fuzzy_m_search
  File "site-packages\aioupnp\protocols\ssdp.py", line 166, in _fuzzy_m_search
aioupnp.fault.UPnPError: M-SEARCH for 192.168.1.128:1900 timed out
2019-01-03 19:45:00,492 ERROR    aioupnp.protocols.ssdp:81: failed to decode SSDP packet from 192.168.1.13:1900 (missing required field server): b'485454502f312e3120323030204f4b0d0a53543a2075706e703a726f6f746465766963650d0a55534e3a20757569643a32383638313531302d326232622d346265302d613531652d3339643830613830663262383a3a75706e703a726f6f746465766963650d0a4c6f636174696f6e3a20687474703a2f2f3139322e3136382e312e31333a353030302f706c7567696e2f646973636f766572792f646973636f766572792e786d6c0d0a43616368652d436f6e74726f6c3a206d61782d6167653d36300d0a0d0a'```

Support for AddAnyPortMapping

Hello,

Thanks for this great library!
I'm using it create port mappings, and I don't particularly care about what external port is used.
Right now I'm using a random port and hoping it isn't already take :P

I just found about AddAnyPortMapping, which takes care of port allocation automatically. Would it be possible to support that?

Thanks!

port mapping conflict in certain cases

I have 4 PCs on my network - 1 of them is configured to use 3335 as the TCP port via the daemon config. First PC grabs 3333. Next PC grabs 3334. Third PC tries to grab 3335, which is already used, and fails to map on 3336 because of the port conflict.

uncaught ConnectionRefusedError in scpd_post

  File "site-packages\aioupnp\upnp.py", line 176, in get_redirects
  File "site-packages\aioupnp\upnp.py", line 154, in get_port_mapping_by_index
  File "site-packages\aioupnp\commands.py", line 222, in GetGenericPortMappingEntry
  File "site-packages\aioupnp\commands.py", line 164, in wrapper
  File "site-packages\aioupnp\protocols\scpd.py", line 149, in scpd_post
  File "asyncio\base_events.py", line 954, in create_connection
  File "asyncio\base_events.py", line 941, in create_connection
  File "asyncio\selector_events.py", line 464, in sock_connect
  File "asyncio\selector_events.py", line 494, in _sock_connect_cb
ConnectionRefusedError: [Errno 10061] Connect call failed ('192.168.1.254', 45553)```

upnp fails when multiple network adapters enabled

My PC has 2 network adapters and if I don't disable one one of them, I see:

2019-01-18 11:28:26,447 WARNING  lbrynet.extras.daemon.Components:665: unable to get external ip from UPnP, checking lbry.io fallback```

prevents startup KeyError: 'NewExternalIPAddress

2019-08-14 20:11:28,691 INFO     lbry.extras.daemon.Components:482: found upnp gateway: AVM Berlin WANConnectionDevice - FRITZ!Box Fon WLAN 7390
2019-08-14 20:11:28,706 ERROR    lbry.extras.daemon.Component:62: Error setting up upnp
Traceback (most recent call last):
  File "lbry\extras\daemon\Component.py", line 55, in _setup
  File "lbry\extras\daemon\Components.py", line 562, in start
  File "lbry\extras\daemon\Components.py", line 493, in _maintain_redirects
  File "site-packages\aioupnp\upnp.py", line 122, in get_external_ip
  File "site-packages\aioupnp\commands.py", line 231, in GetExternalIPAddress
  File "site-packages\aioupnp\commands.py", line 153, in wrapper
  File "site-packages\aioupnp\commands.py", line 42, in recast_return
KeyError: 'NewExternalIPAddress'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.