Giter Club home page Giter Club logo

edk's Introduction

Meltano extension developer kit

Documentation Status | pre-commit.ci status

The Meltano extension developer kit is the fastest way to build custom Meltano extensions. If you're looking to build a custom extractor, loader, or tap then the SDK is actually what you're looking for.

Creating a new extension using the EDK

This repo ships with a copier based template to help developers get and new extension using the Meltano EDK up and running quickly.

Prerequisites for using the template

Install copier:

pipx install copier

Use copier to initialize a new extension

Start a new EDK project using the supplied template (directly from Github):

copier copy gh:meltano/edk my-new-extension

Install the project dependencies:

cd my-new-extension
poetry install

Developing extensions using the EDK

For detailed instructions on developing Meltano EDK extensions, see the Meltano EDK documentation and review the Work-In-Progress Specification.

For working examples of Meltano EDK extensions, see:

edk's People

Contributors

aaronsteers avatar dependabot[bot] avatar douwem avatar edgarrmondragon avatar joshua-janicas avatar menzenski avatar pre-commit-ci[bot] avatar reubenfrankel avatar tayloramurphy avatar willdasilva avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

edk's Issues

feat: Provide optional lock to prevent concurrent pipeline execution

Meltano Version

2.8.0

Python Version

3.8

Bug scope

CLI (options, error messages, logging, etc.)

Operating System

Linux Ubuntu

Description

If we run meltano run tap-something some-mapper target-something and that pipeline is already running, meltano (correctly!) throws an "already running" error and exits. However, if instead we

  1. run meltano run tap-something some-mapper target-something dbt-postgres:run,
  2. wait for the tap+mapper+target block to finish and the dbt-postgres portion to start, and then
  3. run meltano run tap-something some-mapper target-something dbt-postgres:run again

meltano will run the entire pipeline again, ultimately resulting in multiple copies of the same dbt project running at once. :(

If it matters we execute meltano via cron. The tap/mapper/target portion usually only takes a few minutes, but dbt often takes 20+ minutes to run. We had been planning to schedule the job for every 15 minutes and let meltano block concurrent runs when dbt was running long but unfortunately this prevents that.

Code

No response

Docs: Add docs for files-only extensions

The docs today focus on use cases where we wrap an existing tool or library. It could be helpful to also have instructions for developers who just want to replicate the files bundles capability using EDK to deliver.

Related:

Use namespaced package

@WillDaSilva Per: meltano/airflow-ext#1 (review)

I think we should make meltano_extension_sdk into a namespace package named meltano.edk. It would still be versioned/installed separately from Meltano - making it a namespace package only affects the import name (i.e. meltano.edk) and the distribution name meltano-edk.

It's a nice way of organizing things imo. To make it a namespace package, you should only need to update pyproject.toml in the EDK repository so that the name Poetry has for the package is meltano.edk, and then update the directory structure to have be meltano/edk instead of meltano_edk/.

Issue run matatika lab in windows

Try, to run matatika with meltano run matatika lab in windows it's throw error.
meltano version 3.3

Traceback (most recent call last):
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\meltano\edk\extension.py", line 134, in pass_through_invoker
    self.invoke(None, *command_args)
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\matatika_ext\extension.py", line 69, in invoke   
    self.matatika_invoker.run_and_log(command_name, *command_args)
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\matatika_ext\extension.py", line 39, in run_and_log
    return super().run_and_log(sub_command, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\meltano\edk\process.py", line 179, in run_and_log
    result = asyncio.run(self._exec(sub_command, *args))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\base_events.py", line 650, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\meltano\edk\process.py", line 137, in _exec      
    loop.add_signal_handler(
  File "C:\Python311\Lib\asyncio\events.py", line 574, in add_signal_handler
    raise NotImplementedError
NotImplementedError
invoke failed with uncaught exception, please report to maintainer
Traceback (most recent call last):
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\meltano\edk\extension.py", line 134, in pass_through_invoker
    self.invoke(None, *command_args)
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\matatika_ext\extension.py", line 69, in invoke   
    self.matatika_invoker.run_and_log(command_name, *command_args)
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\matatika_ext\extension.py", line 39, in run_and_log
    return super().run_and_log(sub_command, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\meltano\edk\process.py", line 179, in run_and_log    result = asyncio.run(self._exec(sub_command, *args))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\base_events.py", line 650, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\project\meltano-projects\meltano\my_project_feb\.meltano\utilities\matatika\venv\Lib\site-packages\meltano\edk\process.py", line 137, in _exec      
    loop.add_signal_handler(
  File "C:\Python311\Lib\asyncio\events.py", line 574, in add_signal_handler
    raise NotImplementedError
NotImplementedError
Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x000002883F4FFB00>
Traceback (most recent call last):
  File "C:\Python311\Lib\asyncio\base_subprocess.py", line 126, in __del__
    self.close()
  File "C:\Python311\Lib\asyncio\base_subprocess.py", line 104, in close
    proto.pipe.close()
  File "C:\Python311\Lib\asyncio\proactor_events.py", line 108, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "C:\Python311\Lib\asyncio\base_events.py", line 758, in call_soon
    self._check_closed()
  File "C:\Python311\Lib\asyncio\base_events.py", line 519, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000002883F535260>
Traceback (most recent call last):
  File "C:\Python311\Lib\asyncio\proactor_events.py", line 115, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\proactor_events.py", line 79, in __repr__
    info.append(f'fd={self._sock.fileno()}')
                      ^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\windows_utils.py", line 102, in fileno
    raise ValueError("I/O operation on closed pipe")
ValueError: I/O operation on closed pipe
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000002883F535260>
Traceback (most recent call last):
  File "C:\Python311\Lib\asyncio\proactor_events.py", line 115, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\proactor_events.py", line 79, in __repr__
    info.append(f'fd={self._sock.fileno()}')
                      ^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\asyncio\windows_utils.py", line 102, in fileno
    raise ValueError("I/O operation on closed pipe")
ValueError: I/O operation on closed pipe

Meltano EDK utilizing code that is not implemented on Windows

I work with Meltano/DBT in a Windows environment, and containerize the solution into Debian. With the v0.2.0 release I started getting the issue which can be seen by the callstack below.

elt-pipeline: [main ≡ +1 ~1 -0 !]> meltano invoke dbt-snowflake:compile
2024-05-23T11:16:33.760512Z [info     ] Environment 'dev' is active
Extension executing `dbt clean`...
Traceback (most recent call last):
  File "C:\Git\elt-pipeline\.meltano\utilities\dbt-snowflake\venv\Lib\site-packages\meltano\edk\extension.py", line 126, in pass_through_invoker
    self.pre_invoke(None, *command_args)
  File "C:\Git\elt-pipeline\.meltano\utilities\dbt-snowflake\venv\Lib\site-packages\dbt_ext\extension.py", line 72, in pre_invoke
    self.dbt_invoker.run_and_log("clean")
  File "C:\Git\elt-pipeline\.meltano\utilities\dbt-snowflake\venv\Lib\site-packages\meltano\edk\process.py", line 179, in run_and_log
    result = asyncio.run(self._exec(sub_command, *args))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 654, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\Git\elt-pipeline\.meltano\utilities\dbt-snowflake\venv\Lib\site-packages\meltano\edk\process.py", line 137, in _exec
    loop.add_signal_handler(
  File "C:\Program Files\Python311\Lib\asyncio\events.py", line 578, in add_signal_handler
    raise NotImplementedError
NotImplementedError
pre_invoke failed with uncaught exception, please report to maintainer
Traceback (most recent call last):
  File "C:\Git\elt-pipeline\.meltano\utilities\dbt-snowflake\venv\Lib\site-packages\meltano\edk\extension.py", line 126, in pass_through_invoker
    self.pre_invoke(None, *command_args)
  File "C:\Git\elt-pipeline\.meltano\utilities\dbt-snowflake\venv\Lib\site-packages\dbt_ext\extension.py", line 72, in pre_invoke
    self.dbt_invoker.run_and_log("clean")
  File "C:\Git\elt-pipeline\.meltano\utilities\dbt-snowflake\venv\Lib\site-packages\meltano\edk\process.py", line 179, in run_and_log
    result = asyncio.run(self._exec(sub_command, *args))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 654, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\Git\elt-pipeline\.meltano\utilities\dbt-snowflake\venv\Lib\site-packages\meltano\edk\process.py", line 137, in _exec
    loop.add_signal_handler(
  File "C:\Program Files\Python311\Lib\asyncio\events.py", line 578, in add_signal_handler
    raise NotImplementedError
NotImplementedError
Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x00000250E1B10540>
Traceback (most recent call last):
  File "C:\Program Files\Python311\Lib\asyncio\base_subprocess.py", line 126, in __del__
    self.close()
  File "C:\Program Files\Python311\Lib\asyncio\base_subprocess.py", line 104, in close
    proto.pipe.close()
  File "C:\Program Files\Python311\Lib\asyncio\proactor_events.py", line 109, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 762, in call_soon
    self._check_closed()
  File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 520, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x00000250E1B11C60>
Traceback (most recent call last):
  File "C:\Program Files\Python311\Lib\asyncio\proactor_events.py", line 116, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\proactor_events.py", line 80, in __repr__
    info.append(f'fd={self._sock.fileno()}')
                      ^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\windows_utils.py", line 102, in fileno
    raise ValueError("I/O operation on closed pipe")
ValueError: I/O operation on closed pipe
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x00000250E1B11C60>
Traceback (most recent call last):
  File "C:\Program Files\Python311\Lib\asyncio\proactor_events.py", line 116, in __del__
    _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\proactor_events.py", line 80, in __repr__
    info.append(f'fd={self._sock.fileno()}')
                      ^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\asyncio\windows_utils.py", line 102, in fileno
    raise ValueError("I/O operation on closed pipe")
ValueError: I/O operation on closed pipe

Thanks to Derek Visch's insight, I had to pin this EDK back to v0.1.0. It seems that the EDK is using a code here https://github.com/meltano/edk/blob/v0.4.1/meltano/edk/process.py#L139C39-L139C50 that breaks in Windows, but not in Linux environments. Supposedly the fix to this is found here https://stackoverflow.com/questions/35772001/how-to-handle-a-signal-sigint-on-a-windows-os-machine/72637975#72637975.

https://meltano.slack.com/archives/C069CQNHDNF/p1716463234362109

Reducing boilerplate code

I think it would be good to put as much of the core CLI logic into the ExtensionBase class as possible.

The hard part is that we want to do this while still leaving the option open for the developer to add their own commands.

Not a high priority but wanted to log this for discussion and future iterations.

Paradigm to update the scaffold files created by `init` or `initialize`: command for `init --update` or similar

Two considerations here we'd want to solve for in a generic way if possible:

Decision 1. What logic to use when choosing whether to replace, ignore, or merge changes for files that already exist in their destinations.

Parity with files bundles would simply be the ability to specify an array of files to update. Those files specified would overwrite any existing files.

No smart merge logic exists as of today, but something like copier is interesting in how it attempts to apply a mergeable diff patch. (Probably more scope that is feasible in a first iteration.)

Decision 2: What to call the command or subcommand used to update the files or project scaffold

A starting proposal would be to add a flag after the init or initialize command which is already being used to install the files the first time. I think I like --update best personally, but I'll list a few ideas / thought experiments below.

# Init projects files (first time)
my-plugin init                # installs the extension's files

# Init again with the 'update' flag
my-plugin init --update       # updates the extension's files 

# Alternatively:
my-plugin init --replace      # re-initialize the extension's files
my-plugin init --force        # initialize the extension's files (force override)
my-plugin init --interactive  # ask for each file that would be overridden
my-plugin init --prompt       # ask for each file that would be overridden

Related:

Roadmap to support Pydantic v2

This is how we install Airflow in Meltano:

# utilities.meltano.yml

plugins:
  utilities:
  - name: airflow
    variant: apache
    pip_url: git+https://github.com/meltano/airflow-ext.git@main apache-airflow[amazon,opsgenie]==2.7.0
      --constraint https://raw.githubusercontent.com/apache/airflow/constraints-2.7.0/constraints-no-providers-${MELTANO__PYTHON_VERSION}.txt

The problem is we cannot upgrade Airflow to 2.7.1 or later.

The reason is the constraints file of Airflow 2.7.1 or later includes Pydantic v2, see:

And airflow-ext is built on top edk package, which requires Pydantic v1:

[tool.poetry.dependencies]
python = "<3.12,>=3.7"
structlog = ">=21,<24"
PyYAML = "^6.0.0"
pydantic = ">=1.9.0,<2" 
devtools = ">=0.9.0,<1"

(extracted from: https://github.com/meltano/edk/blob/main/pyproject.toml#L44-L49)

Question: Is there a quick way to workaround and upgrade Airflow to 2.7.1 or 2.7.2 in Meltano?

Poetry install in `my-new-extension` returns `virtualenv: error: argument --prompt: expected one argument`

Following the guide in the readme. You are guided to execute:

cd my-new-extension
poetry install

I get

usage: virtualenv [--version] [--with-traceback] [-v | -q] [--read-only-app-data] [--app-data APP_DATA] [--reset-app-data] [--upgrade-embed-wheels] [--discovery {builtin}] [-p py] [--try-first-with py_exe]
                  [--creator {builtin,cpython3-posix,venv}] [--seeder {app-data,pip}] [--no-seed] [--activators comma_sep_list] [--clear] [--no-vcs-ignore] [--system-site-packages] [--symlinks | --copies] [--no-download | --download]
                  [--extra-search-dir d [d ...]] [--pip version] [--setuptools version] [--wheel version] [--no-pip] [--no-setuptools] [--no-wheel] [--no-periodic-update] [--symlink-app-data] [--prompt prompt] [-h]
                  dest
virtualenv: error: argument --prompt: expected one argument

is it just me?

Migrate Docs Website Google Analytics to GA4

Related to https://github.com/meltano/internal-data/issues/89

We need to migrate to the new GA4 property. This is my understanding of what we need to do:

It sounds like we click some buttons in the GA UI to create a new GA4 "property" (i.e. entity in GA, each one of our websites is currently its own property) for the thing we want to migrate, if we dont do this manually theyll soon do it for us automatically. Then to activate it and start sending our events to the new GA4 we need to update our javascript snippets that send the events and include a "tag" manually or use google tag manager (I'm not yet sure what that is). After that point we'll have events flowing to our new "property".

Evaluate/test `copier` based templates

Per out of band discussion with @aaronsteers. We've been interested in using copier as a replacement for cookiecutter due to its built-in support for updates.

Since its still "early days" for EDK we should test a copier based implementation and see if it can replace a cookiecutter based one.

Feat: Allow plugins to declare a default command

Currently if there is no command specified in meltano run or meltano invoke, meltano will use the executable at the plugin level.

Context

For the new plugin SDK, we want to be able to have a passthrough so that meltano invoke my-dbt run "just works" even if the actual syntax is my-plugin invoke run.

We want meltano to run this:

my-plugin invoke run

As a result of us executing this:

meltano invoke my-plugin run

Disambiguation / Pre-Analysis Needed

We should test if a viable alternative would be to have the plugin declare top-level args such as args: invoke, and if that would solve this usage pattern without needing a new feature.

EDK logging

The EDK should provide plugins that use it with logging facilities.

An example of an issue that can arise without such facilities provided:

(cron-ext) will ~/meltano/cron-ext (main) $ meltano invoke cron
2022-09-16T19:21:11.159996Z [info     ] Environment 'dev' is active
Unable to list crontab entries.

The first log line is from Meltano. The second is from calling structlog.get_logger(APP_NAME).error.

The EDK should ensure that the plugins that use it emit logs that match the logging configuration the Meltano instance that is calling it. Maybe to do this the EDK should provide a get_logger function that provides an appropriately configured logger.

`copier gh:meltano/edk my-new-extension`

New developer here, not sure if it's me but I think
copier gh:meltano/edk my-new-extension
should be
copier copy gh:meltano/edk my-new-extension
in [README.md](https://github.com/meltano/edk/blob/main/README.md)

Ctrl+C not passed to subprocesses properly

Background

I have a used the EDK to build an extension (matatika_extension) that wraps Docker Compose.
I am using Python 3.7 on Ubuntu 22.04 LTS.

Expected behaviour

When I run matatika_extension invoke up (alias for docker compose up) and then cancel the process with Ctrl+C, I expect to see the procedure that stops the service containers.

^CGracefully stopping... (press Ctrl+C again to force)
Aborting on container exit...
[+] Running 3/3
 ⠿ Container matatika-ext-app-1      Stopped                                                                                                                           10.1s
 ⠿ Container matatika-ext-catalog-1  Stopped                                                                                                                           10.4s
 ⠿ Container matatika-ext-db-1       Stopped                                                                                                                            0.1s
canceled

Actual behaviour

Containers are not stopped once started before the extension process was cancelled (visible with docker ps). Cancelling the process immediately exits with the following message:

^C/home/linuxbrew/.linuxbrew/opt/[email protected]/lib/python3.7/asyncio/unix_events.py:878: RuntimeWarning: A loop is being detached from a child watcher with pending handlers
  RuntimeWarning)

Aborted!

Potential solution(s)

It seems like the SIGINT signal from Ctrl+C is not being passed the subprocess. Propagating this may fix the issue.

Improve docs and handling of static assets

  • Include clearer (inline of pyproject.toml) comments documenting how to include static asset.
  • Provide utility functions that help deploy static assets into $MELTANO_PROJECT_ROOT (or wherever)

bug: bad meltano_extension_sdk.logging import

Looks like I missed updating an import in the cookiecutter scaffolding.

(ext-dev) ➜  extdev meltano invoke superset_ext db upgrade
Traceback (most recent call last):
  File "/Users/syn/projects/meltano-projects/extdev/.meltano/utilities/superset_ext/venv/bin/superset_invoker", line 5, in <module>
    from superset_ext.pass_through import pass_through_cli
  File "/Users/syn/projects/ext-superset/superset_ext/pass_through.py", line 5, in <module>
    from meltano_extension_sdk.logging import pass_through_logging_config
ModuleNotFoundError: No module named 'meltano_extension_sdk'

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.