{"id":18510047,"url":"https://github.com/lfex/py","last_synced_at":"2025-09-17T20:33:29.684Z","repository":{"id":24994954,"uuid":"28413708","full_name":"lfex/py","owner":"lfex","description":"Distributed Python for the Erlang Ecosystem","archived":false,"fork":false,"pushed_at":"2016-03-20T21:44:18.000Z","size":1050,"stargazers_count":221,"open_issues_count":18,"forks_count":13,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-07-30T01:11:36.722Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"gannasong/gannasong.github.io","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lfex.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-23T18:43:41.000Z","updated_at":"2025-01-22T03:24:54.000Z","dependencies_parsed_at":"2022-08-20T19:40:38.099Z","dependency_job_id":null,"html_url":"https://github.com/lfex/py","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/lfex/py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfex%2Fpy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfex%2Fpy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfex%2Fpy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfex%2Fpy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lfex","download_url":"https://codeload.github.com/lfex/py/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfex%2Fpy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275658777,"owners_count":25504780,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-17T02:00:09.119Z","response_time":84,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T15:20:21.544Z","updated_at":"2025-09-17T20:33:29.624Z","avatar_url":"https://github.com/lfex.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"# py\n\n*Distributed Python for the Erlang Ecosystem*\n\n\u003cimg src=\"resources/images/Python-logo-notext-small.png\"/\u003e\n\n\n## Table of Contents\n\n* [Introduction](#introduction-)\n* [Requirements](#requirements-)\n* [Setup](#setup-)\n* [API Usage](#api-usage-)\n  * [Metadata](#metadata-)\n  * [Module Level](#module-level-)\n    * [Calling Functions](#calling-functions-)\n    * [Module Constants](#module-constants-)\n  * [Objects](#objects-)\n    * [Instantiation](#instantiation-)\n    * [Calling Methods](#calling-methods-)\n    * [Attribute Values](#attribute-values-)\n    * [Operations on Objects](#operations-on-objects-)\n  * [Experimental Dotted Name Support](#experimental-dotted-name-support-)\n  * [ErlPort Pass-Through](#erlport-pass-through-)\n  * [Builtins](#builtins-)\n  * [Operators](#operators-)\n  * [Non-Python Additions](#non-python-additions-)\n  * [Missing Functions](#missing-functions-)\n  * [Erlang](#erlang-)\n* [Architecture](#architecture-)\n  * [Overview](#overview-)\n  * [Erlang Components](#erlang-components-)\n  * [Python Components](#python-components-)\n* [Controlling the Python Servers](#controlling-the-python-servers-)\n  * Erlang Configuration\n  * Python Configuration\n  * Start, Stop, and Restart\n  * Dynamically Adding More Python Servers\n  * Automatic Restarts\n  * Python Server Schedulers\n* [Executing Code in Parallel](#executing-code-in-parallel-)\n  * Identical Calls\n  * Scatter/Gather\n* [Distributed Python](#distributed-python-)\n  * Starting Remote Python Servers\n  * Executing Python on Remote Servers\n\n## Introduction [\u0026#x219F;](#table-of-contents)\n\nThis project provides two key features:\n\n1. An easier interface to Python, wrapping\n   [ErlPort](http://erlport.org/docs/python.html) calls. This lets you do the\n   following very easily:\n    * Make module-level calls\n    * Get module-level constants\n    * Instantiate objects\n    * Call object methods\n    * Get object attributes\n    * Call builtins and operators with convenient wrappers\n1. A means of running Python in a simple distributed context using all the\n   well-known strengths of Erlang (fault-tolerance, scalability,\n   concurrency, soft real-time, etc.).\n\nWhat LFE py is not:\n\n* A PaaS; if that's what you're interested in, please take a look at\n  [CloudI](http://cloudi.org/).\n* A framework for pipelining jobs across distributed data (e.g., mapreduce).\n  For that, see the [Disco project](http://discoproject.org/).\n* A language-agnostic, general-purpose ports server. LFE py is, in fact,\n  built on one of those: [ErlPort](http://erlport.org/)!\n\nLFE py was originally part of the\n[lsci project](https://github.com/lfex/lsci), but was split out due to the\nErlPort/Python-wrapping code being generally useful for all sorts of projects,\nnot just scientific computing in Erlang/LFE.\nThis bit of background should give further insight\ninto the use cases LFE py was intended to address: scientific and (more\nrecently) general computing in Python from the Erlang VM, with a focus on\ninteractive workflows common in academic, research, and startup R\u0026D\nenvironments. Just the sort of thing that\n[IPython](http://ipython.org/) excels at, minus the GUIs :-)\n\n\n## Requirements [\u0026#x219F;](#table-of-contents)\n\nTo use py, you need the following:\n\n* [lfetool](http://docs.lfe.io/quick-start/1.html) and [rebar](https://github.com/rebar/rebar)\n  (used by ``make`` targets to automatically set ``ERL_LIBS`` for deps)\n* [Python 3](https://www.python.org/downloads/)\n* ``wget`` (used to download ``get-pip.py``)\n\n\n## Setup [\u0026#x219F;](#table-of-contents)\n\nFor now, just run it from a git clone:\n\n```bash\n$ git clone git@github.com:lfex/py.git\n$ cd py\n$ make\n```\n\nActivate the Python virtualenv that was created by the ``make`` command you\njust ran. Then start up the LFE REPL:\n\n```bash\n$ . ./python/.venv/bin/activate\n$ make repl-no-deps\n```\n\nNote that the ``repl`` and ``repl-no-deps`` make targets automatically start up\nthe py (and thus ErlPort) Erlang Python server. If you run the REPL without\nthese ``make`` targets, you'll need to manually start things:\n\n```bash\n$ lfetool repl lfe -s py\n```\n\n\n## API Usage [\u0026#x219F;](#table-of-contents)\n\nBelow we show some basic usage of py from both LFE and Erlang. In a\nseparate section a list of docs are linked showing detailed usage of wrapped\nlibraries.\n\n\n### Metadata [\u0026#x219F;](#table-of-contents)\n\nFirst things first: let's make sure that you have the appropriate versions\nof things -- in particular, let's confirm that you're running Python 3:\n\n```cl\n\u003e (py-util:get-versions)\n(#(erlang \"17\")\n #(emulator \"6.2\")\n #(driver-version \"3.1\")\n #(lfe \"0.9.0\")\n #(erlport \"0.9.8\")\n #(py \"0.0.1\")\n #(python\n   (\"3.4.2 (v3.4.2:ab2c023a9432, Oct  5 2014, 20:42:22)\"\n    \"[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\")))\n```\n\n\n### Module Level [\u0026#x219F;](#table-of-contents)\n\nThe following sub-sections describe module-level operations.\n\n\n#### Calling Functions [\u0026#x219F;](#table-of-contents)\n\n```cl\n\u003e (py:func 'os 'getcwd)\n\"/Users/yourname/lab/erlang/py\"\n\u003e (py:func 'datetime.datetime 'now)\n#(\"datetime\" #(2014 12 23 16 57 11 693773 undefined))\n```\n\nNote that strings in arguments need to be converted to binary:\n\n```cl\n\u003e (py:func 'os.path 'isfile '(#b(\"/tmp\")))\nfalse\n\u003e (py:func 'os.path 'isdir '(#b(\"/tmp\")))\ntrue\n```\n\nKeyword arguments are passed as proplists, e.g.,\n``'(#(key1 val1) #(key2 val2))``. In the next example we'll pass a string (as\na binary) which represents a binary number. We'll give ``int`` the keyword of\n``base``, since we're not going to use the default decimal base (10):\n\n```cl\n(py:func 'builtins 'int '(#b(\"101010\")) '(#(base 2)))\n42\n```\n\n\n#### Module Constants [\u0026#x219F;](#table-of-contents)\n\n```cl\n\u003e (py:const 'math 'pi)\n3.141592653589793\n```\n\nOptionally, you may provide a type:\n\n```cl\n\u003e (py:const 'math 'pi 'float)\n3.141592653589793\n\u003e (py:const 'math 'pi 'int)\n3\n\u003e (py:const 'math 'pi 'str)\n\"3.141592653589793\"\n```\n\n\n### Objects [\u0026#x219F;](#table-of-contents)\n\nThe following sections describe how to work with Python objects.\n\n\n#### Instantiation [\u0026#x219F;](#table-of-contents)\n\nWith no arguments passed to the constructor:\n\n```cl\n\u003e (py:init 'builtins 'dict)\n#(\"dict\" ())\n\u003e (py:init 'collections 'UserDict)\n#(\"UserDict\" ())\n```\n\nWith args:\n\n```cl\n\u003e (py:init 'datetime 'date '(1923 4 2))\n#(\"date\" #(1923 4 1))\n```\n\n\n#### Calling Methods [\u0026#x219F;](#table-of-contents)\n\nTo call a method, we need an object. Let's return to the date example\nabove:\n\n```cl\n\u003e (set now (py:func 'datetime.datetime 'now))\n#(\"datetime\" #(2014 12 23 23 14 37 677463 undefined))\n```\n\nThe tuple representing a date time object has been saved as the ``now``\nvariable in the REPL. Let's call some methods:\n\n```cl\n\u003e (py:method now 'strftime '(#b(\"%Y.%m.%d %H:%M:%S\")))\n\"2014.12.23 23:14:37\"\n```\n\n#### Attribute Values [\u0026#x219F;](#table-of-contents)\n\nContinuing with that same object:\n\n```cl\n\u003e (py:attr now 'year)\n2014\n\u003e (py:attr now 'microsecond)\n677463\n```\n\n\n#### Operations on Objects [\u0026#x219F;](#table-of-contents)\n\nLet's get another time ... and give our other variable a better name:\n\n```cl\n\u003e (set later (py:func 'datetime.datetime 'now))\n#(\"datetime\" #(2014 12 23 23 21 25 714474 undefined))\n\u003e (set earlier now)\n#(\"datetime\" #(2014 12 23 23 14 37 677463 undefined))\n```\n\nLet's use the two objects in a calculation:\n\n```cl\n\u003e (set diff (py:sub later earlier))\n#(\"timedelta\" 0 408 37011)\n\u003e (py:attr diff 'seconds)\n408\n```\n\nWe can get that in minutes in LFE/Erlang:\n\n```cl\n\u003e (/ (py:attr diff 'seconds) 60)\n6.8\n```\n\n\n### Experimental Dotted Name Support [\u0026#x219F;](#table-of-contents)\n\nThere is currently tentative support for dotted names in the following\ncalls:\n\n * ``(py:const ...)``\n * ``(py:func ...)``\n * ``(py:init ...)``\n\nExamples:\n\n```cl\n\u003e (py:const 'math.pi)\n3.141592653589793\n```\n\n```cl\n\u003e (py:func 'datetime.datetime.now)\n#(\"datetime\" #(2014 12 29 0 47 30 334180 undefined))\n\u003e (py:func 'math.pow '(2 16))\n65536.0\n\u003e (py:func 'builtins.int '(#b(\"101010\")) '(#(base 2)))\n42\n```\n\n```cl\n\u003e (py:init 'collections.UserDict)\n#(\"UserDict\" ())\n\u003e (py:init 'collections.UserDict '() '(#(\"a\" 1) #(\"b\" 2)))\n#(\"UserDict\" (#(\"b\" 2) #(\"a\" 1)))\n```\n\nThough this is offered, it isn't really encouraged, since there will\nnecessarily be inconsistencies in usage. Dotted notation can be used with\n``const``, ``func``, and ``init`` but not with objects (i.e., not with\n``method`` and ``attr``). This mixing of styles could get confusing and\nyou may think you have a bug in your code when, in fact, you just can't use\ndotted names with instantiated objects (since in LFE it's just a variable\nname, not an actual object).\n\nFinally, with this code in place, many more function calls are incurred\nregardless of whether dotted notation is used. For this reason and the\ndiscouragement against use above, this feature is on the short list for\ngetting axed. Don't count on it being around ...\n\n\n### ErlPort Pass-Through [\u0026#x219F;](#table-of-contents)\n\nIf for any reason you would like to skip the LFE py wrappers and call directly\nto ErlPort, you may do so:\n\n```cl\n\u003e (py:pycall 'datetime 'datetime.now)\n(\"datetime\" #(2014 12 25 20 44 4 673150 undefined))\n\u003e (py:pycall 'datetime 'datetime '(1923 4 2 0 0 0))\n#(\"datetime\" #(1923 4 2 0 0 0 0 undefined))\n```\n\nThese make direct calls to ErlPort's ``python:call`` function, but supply the\nrequired Python server ``pid`` behind the scenes.\n\n\n### Builtins [\u0026#x219F;](#table-of-contents)\n\nIn several of the examples above, we made calls to the ``builtins`` module\nlike so:\n\n```cl\n\u003e (py:init 'builtins 'dict)\n#(\"dict\" ())\n\u003e (py:func 'builtins 'int '(#b(\"101010\")) '(#(base 2)))\n42\n```\n\nLFE py actually provides wrappers for these, making such calls much easier.\n\n```cl\n\u003e (py:dict)\n#(\"dict\" ())\n\u003e (py:dict '(#(\"a\" 1) #(\"b\" 2)))\n#(\"dict\" (#(\"b\" 2) #(\"a\" 1)))\n\u003e (py:int #b(\"101010\") '(#(base 2)))\n42\n```\n\nMore examples:\n\n```cl\n\u003e (py:any '(true true false false false true))\ntrue\n\u003e (py:all '(true true false false false true))\nfalse\n\u003e (py:all '(true true true))\ntrue\n\u003e (py:pow 6 42)\n481229803398374426442198455156736\n\u003e (py:round 0.666666667 5)\n0.66667\n\u003e (py:range 7 42)\n#($erlport.opaque python\n  #B(128 2 99 95 95 98 117 105 108 ...))\n\u003e (py:len (py:range 7 42))\n35\n\u003e (py:pylist (py:range 7 42))\n(7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30\n 31 32 33 34 35 36 ...)\n\u003e (erlang:length (py:pylist (py:range 7 42)))\n35\n```\n\n\n### Operators [\u0026#x219F;](#table-of-contents)\n\nIt will often be the case that you want to operate on the Python data\nstructures obtained via the LFE py calls directly in Python, without\ntranslating them into LFE/Erlang. The Python ``operator`` module is wrapped\nfor your convenience in these cases.\n\nExamples:\n\n```cl\n\u003e (py:add 37 5)\n42\n\u003e (py:mul 7 6)\n42\n\u003e (py:sub -108 -150)\n42\n\u003e (py:truediv 462 11)\n42.0\n\u003e (py:floordiv 462 11)\n42\n```\n\nEquality:\n\n```cl\n\u003e (py:gt 7 6)\ntrue\n\u003e (py:le 7 6)\nfalse\n\u003e (py:eq 42 42)\ntrue\n```\n\nBitwise operations:\n\n```cl\n\u003e (py:and- 60 13)\n12\n\u003e (py:or- 60 13)\n61\n\u003e (py:xor 60 13)\n49\n\u003e (py:inv 60)\n-61\n\u003e (py:rshift 60 2)\n15\n\u003e (py:lshift 60 2)\n240\n```\n\n\n#### Non-Python Additions [\u0026#x219F;](#table-of-contents)\n\nSo as not to stomp on the LFE function ``(list ...)``, the Python ``list``\nbuiltin has been aliased to the ``pylist`` function, e.g.:\n\n```cl\n\u003e (py:pylist)\n()\n\u003e (py:pylist '(1 2 3 4))\n(1 2 3 4)\n```\n\n``(py:dir ...)`` and ``(py:vars ...)`` return elided lists, so you won't see\ncomplete results that are longer than 28 elements. If you wish to see\neverything, you may call ``(py:pdir)`` and ``(py:pvars)``, respectively.\n\n``(py:repr)`` provides wrapping for the Python builtin ``repr``. If you would\nlike to see a representation of the pickled Python data in LFE, you may use\nthe ``(py:prepr)`` function.\n\n\n### Missing Functions [\u0026#x219F;](#table-of-contents)\n\nAny Python function that does in-place modification of objects is not included.\nLFE py will eventually provide analogs for in-place functions that return a new\ndata set or object.\n\n\n### Erlang [\u0026#x219F;](#table-of-contents)\n\nWe can, of course, do all of this from Erlang:\n\n```bash\n$ make shell-no-deps\n```\n\n```erlang\n1\u003e 'py-util':'get-versions'().\n[{erlang,\"17\"},\n {emulator,\"6.2\"},\n {'driver-version',\"3.1\"},\n {lfe,\"0.9.0\"},\n {'lfe-py',\"0.0.1\"},\n {python,[\"3.4.2 (v3.4.2:ab2c023a9432, Oct  5 2014, 20:42:22)\",\n          \"[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\"]}]\n2\u003e py:func(os, getcwd).\n\"/Users/oubiwann/Dropbox/lab/erlang/py\"\n3\u003e py:func('datetime.datetime', now).\n{\"datetime\",{2014,12,25,23,16,14,979696,undefined}}\n4\u003e py:func('os.path', isfile, [\u003c\u003c\"/tmp\"\u003e\u003e]).\nfalse\n5\u003e py:func('os.path', isdir, [\u003c\u003c\"/tmp\"\u003e\u003e]).\ntrue\n6\u003e py:func(builtins, int, [\u003c\u003c\"101010\"\u003e\u003e], [{base, 2}]).\n42\n7\u003e py:const(math, pi).\n3.141592653589793\n8\u003e py:const(math, pi, float).\n3.141592653589793\n9\u003e py:const(math, pi, int).\n3\n10\u003e py:const(math, pi, str).\n\"3.141592653589793\"\n11\u003e py:init(builtins, dict).\n{\"dict\",[]}\n12\u003e py:init(collections, 'UserDict').\n{\"UserDict\",[]}\n13\u003e py:init(datetime, date, [1923, 4, 2]).\n{\"date\",{1923,4,2}}\n```\n\n```erlang\n14\u003e Now = py:func('datetime.datetime', now).\n{\"datetime\",{2014,12,25,23,16,57,146812,undefined}}\n15\u003e py:method(Now, strftime, [\u003c\u003c\"%Y.%m.d %H:%M:%S\"\u003e\u003e]).\n\"2014.12.d 23:16:57\"\n16\u003e py:attr(Now, year).\n2014\n17\u003e py:attr(Now, microsecond).\n146812\n```\n\n```erlang\n18\u003e Later = py:func('datetime.datetime', now).\n{\"datetime\",{2014,12,25,23,19,51,934212,undefined}}\n19\u003e Earlier = Now.\n{\"datetime\",{2014,12,25,23,16,57,146812,undefined}}\n20\u003e Diff = py:sub(Later, Earlier).\n{\"timedelta\",{0,174,787400}}\n21\u003e py:attr(Diff, seconds).\n174\n22\u003e py:attr(Diff, seconds) / 60.\n2.9\n```\n\n```erlang\n23\u003e py:pycall(datetime, 'datetime.now').\n{\"datetime\",{2014,12,25,23,24,46,525495,undefined}}\n24\u003e py:pycall(datetime, datetime, [1923, 4, 2, 0, 0, 0]).\n{\"datetime\",{1923,4,2,0,0,0,0,undefined}}\n```\n\n```erlang\n25\u003e py:dict().\n{\"dict\",[]}\n26\u003e py:dict([{\"a\", 1}, {\"b\", 2}]).\n{\"dict\",[{\"b\",2},{\"a\",1}]}\n27\u003e py:int(\u003c\u003c\"101010\"\u003e\u003e, [{base, 2}]).\n42\n28\u003e py:any([true, true, false, false, false, true]).\ntrue\n29\u003e py:all([true, true, false, false, false, true]).\nfalse\n30\u003e py:all([true, true, true]).\ntrue\n31\u003e py:pow(6, 42).\n481229803398374426442198455156736\n32\u003e py:round(0.666666667, 5).\n0.66667\n33\u003e py:range(7, 42).\n{'$erlport.opaque',python,\n                   \u003c\u003c128,2,99,95,95,98,117,105,108,...\u003e\u003e}\n34\u003e py:len(py:range(7, 42)).\n35\n35\u003e py:pylist(py:range(7, 42)).\n[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,\n 27,28,29,30,31,32,33,34,35|...]\n36\u003e length(py:pylist(py:range(7, 42))).\n35\n```\n\n```erlang\n37\u003e py:add(37, 5).\n42\n38\u003e py:mul(7, 6).\n42\n39\u003e py:sub(-108, -150).\n42\n40\u003e py:truediv(462, 11).\n42.0\n41\u003e py:floordiv(462, 11).\n42\n42\u003e py:gt(7, 6).\ntrue\n43\u003e py:le(7, 6).\nfalse\n44\u003e py:eq(42, 42).\ntrue\n45\u003e py:'and-'(60, 13).\n12\n46\u003e py:'or-'(60, 13).\n61\n47\u003e py:'xor'(60, 13).\n49\n48\u003e py:inv(60).\n-61\n49\u003e py:rshift(60, 2).\n15\n50\u003e py:lshift(60, 2).\n240\n```\n\n```erlang\n51\u003e py:pylist().\n[]\n52\u003e py:pylist([1, 2, 3, 4]).\n[1,2,3,4]\n```\n\n\n## Architecture [\u0026#x219F;](#table-of-contents)\n\n\n### Overview [\u0026#x219F;](#table-of-contents)\n\nHere is a high-level diagram of the LFE py architecture:\n\n```\n+-------------------------------------------+\n|                                           |\n| +-----------+ +-----------+ +-----------+ |\n| | py Worker | | py Worker | | py Worker | |\n| +------+----+ +-----+-----+ +-----+-----+ |\n|        |            |             |       |\n|        |            |             |       |\n|        |       +----+---+         |       |\n|        |       |        |         |       |\n|        +-------+ py+sup +---------+       |\n|                |        |                 |\n|                +----+---+                 |\n|                     |                     |\n|                +----+---+                 |\n|                |        |                 |\n|                | py+app |                 |\n|                |        |                 |\n|                +--------+                 |\n|                                           |\n|                  LFE py                   |\n+---------------------+---------------------+\n|        LFE          |       ErlPort       |\n+---------------------+---------------------+\n|                 Erlang/OTP                |\n+-------------------------------------------+\n```\n\nEach py Worker is actually a wrapper for an ErlPort ``gen_server`` which starts\nup Python interpreter. LFE py is only designed to work with Python 3. Both\nErlPort and it provide Python 3 modules for use in the interpreters started\nby the workers. They have the following conceptual structure:\n\n```\n+----------------+\n|                |\n|  +----------+  |\n|  | Encoders |  |\n|  +----------+  |\n|  +----------+  |\n|  | Decoders |  |\n|  +----------+  |\n|                |\n|   LFE py lib   |\n+----------------+\n|   ErlPort lib  |\n+----------------+\n|    Python 3    |\n+----------------+\n```\n\nAs depicted above, when the LFE py/ErlPort Python server starts, it brings up\na Python 3 interpreter. LFE py configures the ``PYTHONPATH`` for\nErlPort so that the custom encoder, decoder, and object helper Python modules\nare available for use by all Python calls issued to the workers.\n\n\n### Erlang Components [\u0026#x219F;](#table-of-contents)\n\nWorking our way up from the diagram, here are references for Erlang/OTP\ncomponents of LFE py:\n\n* [Erlang/OTP](http://learnyousomeerlang.com/what-is-otp)\n* [Erlang/OTP apps](http://learnyousomeerlang.com/building-applications-with-otp)\n* [How ErlPort works](http://erlport.org/docs/#id3)\n* [LFE](http://en.wikipedia.org/wiki/LFE_%28programming_language%29)\n* [py-app](https://github.com/lfex/py/blob/master/src/py-app.lfe)\n* [py-app](https://github.com/lfex/py/blob/master/src/py-sup.lfe)\n* [py Workers](https://github.com/lfex/py/blob/master/src/py.lfe#L7)\n\n\n### Python Components [\u0026#x219F;](#table-of-contents)\n\nAnd here are references for the Python components in LFE py:\n\n* [Python 3](https://docs.python.org/3/)\n* [ErlPort Python library](https://github.com/hdima/erlport/tree/master/priv/python3/erlport)\n* [LFE py Python library](https://github.com/lfex/py/tree/master/python/lfe)\n* [py Encoders](https://github.com/lfex/py/blob/master/python/lfe/encoders.py)\n* [py Decoders](https://github.com/lfex/py/blob/master/python/lfe/decoders.py)\n\n\n## Controlling the Python Servers [\u0026#x219F;](#table-of-contents)\n\n### Erlang Configuration [\u0026#x219F;](#table-of-contents)\n\n### Python Configuration [\u0026#x219F;](#table-of-contents)\n\n### Start, Stop, and Restart [\u0026#x219F;](#table-of-contents)\n\n### Dynamically Adding More Python Servers [\u0026#x219F;](#table-of-contents)\n\n### Automatic Restarts [\u0026#x219F;](#table-of-contents)\n\n### Python Server Schedulers [\u0026#x219F;](#table-of-contents)\n\n\n## Executing Code in Parallel [\u0026#x219F;](#table-of-contents)\n\n### Identical Calls [\u0026#x219F;](#table-of-contents)\n\n### Scatter/Gather [\u0026#x219F;](#table-of-contents)\n\n\n## Distributed Python [\u0026#x219F;](#table-of-contents)\n\n### Starting Remote Python Servers [\u0026#x219F;](#table-of-contents)\n\n### Executing Python on Remote Servers [\u0026#x219F;](#table-of-contents)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flfex%2Fpy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flfex%2Fpy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flfex%2Fpy/lists"}